我正在编写一个文件系统搜索器,它将目录(通常是Windows驱动器)和过滤器作为输入,并在每个输入目录中搜索与输入过滤器匹配的文件。依次地,我这样实现了它(相关代码的简化版本):
foreach (var dir in Directories) {
Console.WriteLine("Searching: " + dir);
var s = new FSSearcher(dir, FileTypes, Keywords, SearchContents);
s.Search();
}
class FSSearcher {
[...]
private static List<string> Filetypes;
private static List<string> Keywords;
private static List<string> Results;
private static bool searchContents;
private static string SearchDirectory;
[...]
public FSSearcher(string d, List<string> f, List<string> k, bool s) {
SearchDirectory = d;
Filetypes = f;
Keywords = k;
Results = new List<string>();
searchContents = s;
}
public void Search() {
if (Directory.Exists(SearchDirectory)) {
Console.WriteLine("Searching dir: " + SearchDirectory);
[...]
}
}
}
此代码正常且按预期工作。循环结束时无需收集任何结果。 FSSearcher是一个“独立类”。现在,我想并行运行此foreach
循环(不同的物理驱动器,因此并发不应受到I / O的限制),因此我将上面的代码更改为以下代码(同样是简化代码):
var ConcurrentDirectories = new ConcurrentBag<string>();
ConcurrentDirectories.Add("C:\\");
ConcurrentDirectories.Add("Z:\\");
var options = new ParallelOptions { MaxDegreeOfParallelism = ConcurrentDirectories.Count };
Parallel.ForEach(ConcurrentDirectories, options, (dir) => {
Console.WriteLine("Searching in parallel: " + dir);
var s = new FSSearcher(dir, FileTypes, Keywords, SearchContents);
s.Search();
});
我假设变量s
对于每个迭代都是局部的。然而,这种情况并非如此。上面的代码提供以下输出:
Searching in parallel: Z:\
Searching in parallel: C:\
Searching dir: C:\
Searching dir: C:\ <=== this is expected to be Z:\
似乎发生的是,在一个“迭代”中为s
初始化了Z:\
,然后为C:\
重新分配了另一个FSSearcher实例。然后,Search()
为C:\
的最后一个分配对象调用了两次。
我已经阅读了所有相关的SO帖子以获取解释或解决方案,但似乎找不到解释。这是怎么回事,如何防止它发生?
非常感谢!
答案 0 :(得分:0)
这里的问题是我在static
的构造函数中分配了FSSearch
变量。在构造函数is apparently not thread-safe中分配静态变量。我将代码更改为以下代码,从而使并发问题消失了:
class FSSearcher {
[...]
private List<string> Filetypes;
private List<string> Keywords;
private List<string> Results;
private bool searchContents;
private string SearchDirectory;
[...]
public FSSearcher(string d, List<string> f, List<string> k, bool s) {
this.SearchDirectory = d;
this.Filetypes = f;
this.Keywords = k;
this.Results = new List<string>();
this.searchContents = s;
}
public void Search() {
if (Directory.Exists(SearchDirectory)) {
Console.WriteLine("Searching dir: " + SearchDirectory);
[...]
}
}
}