例如,我有一个包含 3 个项目的列表视图。每个项目都包含一个文件夹路径列和一个文件夹中文件数量的列。
如果我启动一个单独的后台工作人员来计算每个文件夹中的文件,我会得到一个意想不到的结果。我想我已经找到了问题,但我不知道如何解决它。
在下面的示例中,我使用两种不同的方法计算了每个文件夹中的文件;第一种方法为每个文件夹创建一个后台工作程序,每个后台工作程序在计算文件时同时运行。第二种方法创建一个后台工作人员,该工作人员按顺序计算每个文件夹中的文件。连续计数确实有效,而同时计数则无效。
问题似乎出在 GetPicturesConcurrently() 方法中,特别是在以下行中:
fileCounter.DoWork += new DoWorkEventHandler((obj, e) => CountFilesInFolder(item.Text));
似乎正在发生的事情是,实际传递给 CountFilesInFolder(string) 每次调用的字符串最终会使用最后一个创建的后台工作人员的字符串到达方法中;好像 item.Text 中的字符串是通过引用而不是值传递的。所以我最终一遍又一遍地计算同一个文件夹中的文件。
当我在后台工作程序创建时 Break 时,我可以看到每次都传递了正确的字符串;当我在 CountFilesInFolder 上中断时,每次调用都会处理输入的最后一个字符串。
这是一个演示问题的示例:
public partial class Form1 : Form
{
private ConcurrentDictionary<string, int> MyFiles;
private List<string> Folders;
public Form1()
{
MyFiles = new ConcurrentDictionary<string,int>();
Folders = new List<string>();
InitializeComponent();
PopulateListview();
}
private void PopulateListview()
{
ListViewItem item1 = new ListViewItem();
ListViewItem item2 = new ListViewItem();
ListViewItem item3 = new ListViewItem();
item1.Text = @"V:\";
item2.Text = @"D:\";
item3.Text = @"C:\";
item1.SubItems.Add("");
item2.SubItems.Add("");
item3.SubItems.Add("");
listView1.Items.Add(item1);
listView1.Items.Add(item2);
listView1.Items.Add(item3);
}
private void GetPicturesInSeries()
{
Reset();
foreach (ListViewItem item in listView1.Items)
{
Folders.Add(item.Text);
}
BackgroundWorker fileCounter = new BackgroundWorker();
fileCounter.DoWork += new DoWorkEventHandler((obj, e) => GetPictures());
fileCounter.RunWorkerCompleted += new RunWorkerCompletedEventHandler((obj, e) => UpdateCountListView());
fileCounter.RunWorkerAsync();
}
private void GetPicturesConcurrently()
{
Reset();
foreach (ListViewItem item in listView1.Items)
{
BackgroundWorker fileCounter = new BackgroundWorker();
fileCounter.DoWork += new DoWorkEventHandler((obj, e) => CountFilesInFolder(item.Text));
fileCounter.RunWorkerCompleted += new RunWorkerCompletedEventHandler((obj, e) => UpdateCountListView(item.Index));
fileCounter.RunWorkerAsync();
}
}
private void GetPictures()
{
foreach (string folder in Folders)
{
CountFilesInFolder(folder);
}
}
private void CountFilesInFolder(string folder)
{
DirectoryInfo dirInfo = new DirectoryInfo(folder);
IEnumerable<FileInfo> files = dirInfo.EnumerateFiles();
int count = files.Count();
MyFiles.AddOrUpdate(folder, count, (key, oldvalue) => files.Count());
}
private void UpdateCountListView(int index)
{
string key = listView1.Items[index].Text;
int count;
MyFiles.TryGetValue(key,out count);
listView1.BeginUpdate();
listView1.Items[index].SubItems[1].Text = count.ToString();
listView1.EndUpdate();
listView1.Refresh();
}
private void UpdateCountListView()
{
listView1.BeginUpdate();
foreach (ListViewItem item in listView1.Items)
{
string key = item.Text;
int count;
MyFiles.TryGetValue(key, out count);
listView1.Items[item.Index].SubItems[1].Text = count.ToString();
}
listView1.EndUpdate();
listView1.Refresh();
}
private void Reset()
{
listView1.BeginUpdate();
foreach (ListViewItem item in listView1.Items)
{
item.SubItems[1].Text = "";
}
listView1.EndUpdate();
listView1.Refresh();
Folders.Clear();
MyFiles.Clear();
}
}