Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将异步方法传递到Parallel.ForEach_C#_Async Await_Parallel.foreach - Fatal编程技术网

C# 将异步方法传递到Parallel.ForEach

C# 将异步方法传递到Parallel.ForEach,c#,async-await,parallel.foreach,C#,Async Await,Parallel.foreach,我在读关于Parallel.ForEach的文章,其中提到“Parallel.ForEach与传入异步方法不兼容” 因此,为了检查,我编写了以下代码: static async Task Main(string[] args) { var results = new ConcurrentDictionary<string, int>(); Parallel.ForEach(Enumerable.Range(0, 100), async index =>

我在读关于Parallel.ForEach的文章,其中提到“Parallel.ForEach与传入异步方法不兼容”

因此,为了检查,我编写了以下代码:

static async Task Main(string[] args)
{
    var results = new ConcurrentDictionary<string, int>();

    Parallel.ForEach(Enumerable.Range(0, 100), async index =>
    {
        var res = await DoAsyncJob(index);
        results.TryAdd(index.ToString(), res);
    });         

    Console.ReadLine();
}

static async Task<int> DoAsyncJob(int i)
{
    Thread.Sleep(100);
    return await Task.FromResult(i * 10);
}
static async Task Main(字符串[]args)
{
var结果=新的ConcurrentDictionary();
Parallel.ForEach(Enumerable.Range(01100),异步索引=>
{
var res=等待DoAsyncJob(索引);
results.TryAdd(index.ToString(),res);
});         
Console.ReadLine();
}
静态异步任务DoAsyncJob(int i)
{
睡眠(100);
返回等待任务。FromResult(i*10);
}
此代码同时填充
结果
字典

顺便说一下,我创建了一个类型为
concurrentdirectionary
的字典,因为如果我在调试模式下探索它的元素,我会看到元素是按键排序的,我认为元素是被添加的

我想知道我的代码是否有效?如果它“与传入异步方法不兼容”,为什么它工作得很好?

异步方法是启动并返回
任务的方法

你的代码在这里

Parallel.ForEach(Enumerable.Range(0, 100), async index =>
{
    var res = await DoAsyncJob(index);
    results.TryAdd(index.ToString(), res);
});        
并行运行异步方法100次。也就是说,它并行于任务创建,而不是整个任务。当
ForEach
返回时,您的任务正在运行,但不一定完成

您的代码之所以有效,是因为
DoAsyncJob()
实际上不是异步的-您的任务在返回时就完成了
Thread.Sleep()
是一种同步方法
Task.Delay()
是它的异步等价物


理解两者之间的区别。正如其他人已经指出的那样,并行(和
Parallel.ForEach
)用于CPU限制的操作,异步编程是不合适的。

此代码之所以有效,是因为
DoAsyncJob
不是真正的异步方法<代码>异步
不会使方法异步工作。等待一个完成的任务,如
任务返回的任务。FromResult
也是同步的
async Task Main
不包含任何异步代码,这将导致编译器警告


演示
Parallel.ForEach
如何与异步方法一起工作的示例应调用真正的异步方法:

    static async Task Main(string[] args)
    {
        var results = new ConcurrentDictionary<string, int>();

        Parallel.ForEach(Enumerable.Range(0, 100), async index =>
        {
            var res = await DoAsyncJob(index);
            results.TryAdd(index.ToString(), res);
        });  
        Console.WriteLine($"Items in dictionary {results.Count}");
    }

    static async Task<int> DoAsyncJob(int i)
    {
        await Task.Delay(100);
        return i * 10;
    }
没有接受
Func
的重载,它只接受
操作
委托。这意味着它不能等待任何异步操作


异步索引
被接受,因为它是隐式的。就
Parallel.ForEach
而言,它只是一个
操作


结果是,
Parallel.ForEach
触发100个任务,从不等待它们完成。这就是为什么应用程序终止时字典仍然为空。

如果已经有异步工作,则不需要
并行。ForEach

static async Task Main(string[] args)
{

    var results = await new Task.WhenAll(
        Enumerable.Range(0, 100)
        Select(i => DoAsyncJob(I)));

    Console.ReadLine();
}
关于异步作业,您可以一路异步:

static async Task<int> DoAsyncJob(int i)
{
    await Task.Delay(100);
    return await Task.FromResult(i * 10);
}
静态异步任务DoAsyncJob(int i)
{
等待任务。延迟(100);
返回等待任务。FromResult(i*10);
}
更好的是:

static async Task<int> DoAsyncJob(int i)
{
    await Task.Delay(100);
    return i * 10;
}
静态异步任务DoAsyncJob(int i)
{
等待任务。延迟(100);
返回i*10;
}
或者根本没有:

static Task<int> DoAsyncJob(int i)
{
    Thread.Sleep(100);
    return Task.FromResult(i * 10);
}
静态任务DoAsyncJob(int i)
{
睡眠(100);
返回任务.FromResult(i*10);
}

不要这样做<代码>并行。ForEach用于CPU密集型计算,不识别异步方法。它不会等待它们,本质上是将它们转换为
async void
fire-and-forget调用。您的方法无论如何都不是异步的,因此无法说出正确的调用的外观like@PanagiotisKanavos请公布你的信息来源。谢谢
我想知道我的代码是否有效?
否。
为什么它工作得很好?
它不工作,但您没有意识到,因为a)它不执行任何异步工作。
并行。ForEach
-“安排多个线程并行执行以下工作”
异步“好吧,在其他东西完成这个等待的任务之前,这个线程没有什么有用的工作要做。”即使它真的工作了(它没有,正如Panagitotis所说,它是完全同步的),您以一种奇怪的方式组合东西,过度分配资源,然后忽略它们。@Seabizkit没有什么可说服的。没有接受任务的重载。没有重载,就没有办法等待任何异步调用。
async
不会等待任何东西,也不会使任何东西异步运行。
async void
调用不能等待,这就是为什么它们被认为是事件处理程序之外的bug。您在评论中说我可以使用list.Select(item=>Task.Run(…)为什么我应该在Select方法中使用Task.Run()?我可以这样使用:var tasks=Enumerable.Range(0,100)。Select(async index=>{var res=await-DoAsyncJob(index);results.TryAdd(index.ToString(),res);});await-Task.WhenAll(tasks);Async用于释放等待“I/O”的线程"东西,所以在这种情况下,它不会释放一些线程,因为foreach中的操作可能是db访问,例如…你是说如果调用程序aka Parallel.foreach可以返回任务,而事实上它没有返回任务,那么情况就是这样的…所有并行的任务操作都不能正确地实现异步库。是吗我说可以吗?@DmitryS因为你没有解释你真正想做什么。如果你想在后台处理一些数据项而不阻塞,你可以使用
。选择(…=>Task.Run…)
。如果您想测试多个算法并获得第一个结果,同样可以。@Seabizkit
Parallel.ForEach
专门用于数据并行。这就是为什么它不为任务提供任何重载,而是实际使用当前线程进行处理的原因。它
static Task<int> DoAsyncJob(int i)
{
    Thread.Sleep(100);
    return Task.FromResult(i * 10);
}