Parallel.For vs ThreadPool和async / await

时间:2017-02-25 01:16:08

标签: c# async-await task-parallel-library

我试图以异步方式处理5000个文件,而不会使Threadpool不受限制。然而,Parallel.For循环并没有给我一致的正确答案(计数很短),而Task.Run则是。

我在导致这些错误答案的Parallel.For循环中出错了什么?

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static volatile int count = 0;
    static volatile int total = 0;
    static void Main(string[] args)
    {
        Parallel.For(0, 5000, new ParallelOptions { MaxDegreeOfParallelism = 10 },
            async (index) =>
            {
                string filePath = $"C:\\temp\\files\\out{index}.txt";
                var bytes = await ReadFileAsync(filePath);
                Interlocked.Add(ref total, bytes.Length);
                Interlocked.Increment(ref count);
            });
        Console.WriteLine(count);
        Console.WriteLine(total);

        count = 0;
        total = 0;
        List<Task> tasks = new List<Task>();
        foreach (int index in Enumerable.Range(0, 5000))
        {
            tasks.Add(Task.Run(async () =>
            {
                string filePath = $"C:\\temp\\files\\out{index}.txt";
                var bytes = await ReadFileAsync(filePath);
                Interlocked.Add(ref total, bytes.Length);
                Interlocked.Increment(ref count);
            }));
        }
        Task.WhenAll(tasks).Wait();
        Console.WriteLine(count);
        Console.WriteLine(total);
    }
    public static async Task<byte[]> ReadFileAsync(string filePath)
    {
        byte[] bytes = new byte[4096];
        using (var sourceStream = new FileStream(filePath,
                FileMode.Open, FileAccess.Read, FileShare.Read,
                bufferSize: 4096, useAsync: true))
        {
            await sourceStream.ReadAsync(bytes, 0, 4096);
        };
        return bytes;
    }
}

1 个答案:

答案 0 :(得分:9)

Parallel.For并非async知道。

因此,Parallel.For未按预期执行。由于不等待async lambda生成的任务,所有迭代将在创建任务所需的时间内完成,而不是完成它们。

Parallel.For之后,一些迭代仍然会有尚未完成的待处理任务,因此,您对counttotal的添加尚未完成。< / p>

Stephen Toub已经实现了Parallel.ForEach的异步版本。 (ForEachAsync)实施如下:

public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
{
    return Task.WhenAll(
        from partition in Partitioner.Create(source).GetPartitions(dop)
        select Task.Run(async delegate {
            using (partition)
                while (partition.MoveNext())
                    await body(partition.Current);
        }));
}

所以你可能会重写你的循环:

Enumerable.Range(0, 5000).ForEachAsync(10, async (index)=>{
   //$$$
});