.NET PLINQ 与异步性能

.NET PLINQ vs Async Performance

鉴于此 PLINQ 代码:

        public static IEnumerable<Tuple<string, string>> PlinqFileProcessingLimitedCores(int nr_of_cores) 
    {
        string archiveDirectory = @"C:\Dotnet46Examples";

        return (from file in Directory.EnumerateFiles(archiveDirectory, "*.cs", SearchOption.AllDirectories)
                from line in File.ReadLines(file).AsParallel().WithDegreeOfParallelism(nr_of_cores)
                where line.Contains("Console")
                select new Tuple<string, string>(file, line));
    }

其中 returns 所有文件的所有行都包含单词 Console。

我尝试编写更快的异步版本,但是它们都比 PLINQ 慢得多,例如:

        public static async Task<ConcurrentBag<Tuple<string, string>>> FileProcessingAsync()
    {
        string archiveDirectory = @"C:\Dotnet46Examples";
        var bag = new ConcurrentBag<Tuple<string, string>>();
        var tasks = Directory.EnumerateFiles(archiveDirectory, "*.cs", SearchOption.AllDirectories)
               .Select(file => ProcessFileAsync(bag, file));
        await Task.WhenAll(tasks);  
        return bag;
    }

        static async Task ProcessFileAsync(ConcurrentBag<Tuple<string, string>> bag, string file)
    {
        String line;
        using (StreamReader reader = File.OpenText(file))
        {
            while (reader.Peek() >= 0)
            {
                line = await reader.ReadLineAsync(); 
                if (line != null)
                {
                    if (line.Contains("Console"))
                    {
                        bag.Add(new Tuple<string, string>(file, line));
                    }
                }
            }        
        }
    }

为什么异步代码这么慢(在我的笔记本电脑上慢了 1000 倍)? 更好的代码是什么样子的? 这个问题不适合异步吗? 谢谢

您的并行示例是(同步)一次一行将文件读入内存并(并行)搜索文本。这可能是可用的最快解决方案,因为 Windows 上的同步文件 I/O 通常比异步文件快。

I tried to write faster asynch versions

"Asynchronous" 并不代表 "faster"。意思是"does not block the calling thread"。异步代码有额外的开销,因此通常 较慢 。异步代码的好处不是速度,而是速度。它正在释放线程。只有当这些线程有其他工作要做时,这才有好处;例如,在服务器环境中,他们可以处理其他请求。

还有一个问题是 File.OpenText 这样的方法实际上不允许异步访问,所以 ReadLineAsync 实际上做的是 运行 在线程池上同步工作然后异步处理它。但是即使你有一个正确的异步实现,它也不会比同步读取文件更快。