Parallel.ForEach变量不被视为局部变量

时间:2019-10-27 12:46:15

标签: c#-4.0 parallel-processing

我正在编写一个文件系统搜索器,它将目录(通常是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帖子以获取解释或解决方案,但似乎找不到解释。这是怎么回事,如何防止它发生?

非常感谢!

1 个答案:

答案 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);
                [...]

            }
    }
}