我有一个FTP远程文件列表,我试图检索每个文件的文件大小。这些文件位于不同的服务器上。
我有一个方法GetFileSize
,它运行得很好。目前,我通过一个简单的循环一次处理每个项目的列表。
考虑到文件大小的简单请求不会占用大量带宽,我想同时异步执行所有请求。
我尝试为每个项目使用BackgroundWorker
个数组,但结果是只处理了列表中的最后一项:
public static void GetFileSizes(List<string> files)
{
BackgroundWorker[] workers = new BackgroundWorker[files.Count];
for (int i = 0; i < files.Count - 1; i++)
{
workers[i] = new BackgroundWorker();
workers[i].WorkerReportsProgress = false;
workers[i].WorkerSupportsCancellation = true;
workers[i].DoWork += (sender, e) =>
{
long size = Ftp.GetSize(files[i]);
MessageBox.Show(size.ToString());
};
workers[i].RunWorkerAsync();
}
}
还有其他办法吗?
答案 0 :(得分:1)
您无法确保每个匿名方法都捕获不同的变量。 for
循环变量i
对于每个匿名方法都是相同的变量。
由于当前线程做得很少,因此在任一工作者完全运行之前完成整个循环的概率非常高。发生这种情况时,变量i
处于不符合循环条件的第一个值,即file.Count - 1
。
按常规编写循环时,循环结尾处的变量值为file.Count
,因此异常。
有多种方法可以解决这个问题,但恕我直言最好和最简单的方法是捕获文件名而不是索引。这不仅解决了您的直接问题,还将工作人员与实际的files
对象隔离开来,这样如果您在其他地方有错误并在工作人员完成之前修改该对象,则不会影响工作人员。
E.g:
for (int i = 0; i < files.Count - 1; i++)
{
string file = files[i];
workers[i] = new BackgroundWorker();
workers[i].WorkerReportsProgress = false;
workers[i].WorkerSupportsCancellation = true;
workers[i].DoWork += (sender, e) =>
{
long size = Ftp.GetSize(file);
MessageBox.Show(size.ToString());
};
workers[i].RunWorkerAsync();
}
最后,请不要将WorkerReportsProgress
或WorkerSupportsCancellation
属性设置为true
,除非您实际实施工作中的相应行为。
(我假设在您的实际代码中,您不会从工作线程调用MessageBox.Show()
...这也是一个坏主意,至少对于非调试方案而言。)