C#多线程访问问题

3
下面的代码出现了错误 "该进程无法访问文件,因为它正被另一个进程使用"。我对问题感到困惑。我已经以管理员身份运行 Visual Studio,并且没有任何文件在记事本中打开。
    private void Load_Click(object sender, RoutedEventArgs e)
    {
        if (txtInput.Text.Length > 1) {
            //var rootDir = System.IO.Directory.GetCurrentDirectory();
            string rootDir = @"C:\b";
            string search = txtInput.Text.Replace(" ", "");
            List<Thread> searches = new List<Thread>();

            foreach (var file in new DirectoryInfo(rootDir).GetFiles().Where(z => z.LastWriteTime > DateTime.Now.AddDays(-7))) {
                if (file.ToString().Contains(".log")) {
                    searches.Add(new Thread(new ThreadStart(() => AddDropdownItem(file.ToString(),search))));
                }
            }
            //Run ten threads at a time and wait for them to finish
            for (int i = 0; i < searches.Count; i = i + 10) {
                List<Thread> pool = new List<Thread>();
                for (int j = 0; j < 10; j++) {
                    if (i + j < searches.Count) {
                        Thread t = searches[(i + j)];
                        pool.Add(t);
                    }
                }

                foreach (Thread t in pool) {
                    t.Start();
                }

                foreach (Thread t in pool) {
                    t.Join();
                }
            }
        }
    }

    private void AddDropdownItem(string file, string search)
    {
        if (GetFileContent(file.ToString()).Contains(search)) {
            ComboBoxItem item = new ComboBoxItem();
            item.Content = file.ToString();
            Dispatcher.BeginInvoke(new ThreadStart(() => ddFiles.Items.Add(item)));
        }
    }

    private string GetFileContent(string file)
    {
        string path = System.IO.Path.Combine(@"C:\b", file);
        using (FileStream fs = new FileStream(path, FileMode.Open)) {
            return new StreamReader(fs).ReadToEnd();
        }
    }

+1 作为支持,反对未经评论的负评。 - sra
为什么要在字符串上调用.ToString()方法? - thumbmunkeys
2个回答

3
问题很可能是您在 lambda 表达式中捕获循环变量的方式。请记住,闭包捕获的是变量而不是值。因此,AddDropdownItem 方法可能会接收与您所认为的参数 file 不同的值。这是 闭合循环变量 的一个已知行为注意事项。
请更改循环,将循环变量复制到单独的引用中。
foreach (var file in new DirectoryInfo(rootDir).GetFiles().Where(z => z.LastWriteTime > DateTime.Now.AddDays(-7))) 
{
  if (file.ToString().Contains(".log")) 
  {
    var capture = file;
    searches.Add(new Thread(new ThreadStart(() => AddDropdownItem(capture.ToString(),search))));
  }
}

我注意到一个可能无关的问题。看起来你正在从其中一个工作线程创建一个ComboBoxItem。我确实看到你正在将添加操作调度到UI线程上。为了安全起见,我建议确保ComboBoxItem也在UI线程上创建。你现在的方式可能不会引起任何问题,但是我更倾向于将不允许从除UI线程以外的线程访问UI元素的规则推向极致。

太好了!你的第一个答案解决了我的问题,第二个答案解决了它开始工作时发现的另一个bug。非常感谢。 - Tom Squires

1

我看不到“AddDropDownItem”,但我敢打赌你在那里打开文件,而且线程完成后没有关闭文件。仅释放变量(或让它们超出范围并让GC处理)是不够的。在线程完成之前,先显式地关闭文件。


所有的代码都在上面的示例中。我将文件流放在了using块中。我也尝试了显式关闭它,但没有帮助。 - Tom Squires
你在其他地方打开了其他文件吗? - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接