简体   繁体   English

如何从List.ForEach C#进行异步调用

[英]How to make async calls from List.ForEach C#

I have list of items and for each item i want to call the UploadChunkToServer method but in parallel. 我有项目列表,对于每个项目,我想并行调用UploadChunkToServer方法。 There will always be only 4 items in the list. 列表中始终只有4个项目。

if (DataQueue.Count == NumberofChunksToRead)
{
    DataQueue.ToList().ForEach(Chunk =>
    {
        UploadChunkToServer(Chunk);
    });
}

Above is my code call in the method and the method signature is as follows. 上面是我在方法中的代码调用,方法签名如下。

private void UploadChunkToServer(UserCustomFile Chunk)
{
    var client = new RestClient(Helper.GetServerURL());

    var request = new RestRequest("api/fileupload/savechunk", Method.POST);
    request.AddHeader("Content-type", "application/json");
    request.RequestFormat = RestSharp.DataFormat.Json;
    request.AddJsonBody(Chunk);

    client.ExecuteAsync(request, response =>
    {
        if (response.StatusCode != System.Net.HttpStatusCode.OK)
        {
            throw new Exception(response.ErrorMessage);
        }
        else
        {
            ChunkStatuses.Add(Chunk.ChunkID);
        }
    });
}

I have tried to add the async operator but no luck. 我试图添加异步运算符,但没有运气。 Any ideas how this could be accomplished? 有什么想法可以实现吗?

Perhaps you can make use of Parallel and ForEach in specific: 也许您可以在特定情况下使用Parallel和ForEach:

Parallel.ForEach(DataQueue.ToList(), Chunk =>
                 {
                      UploadChunkToServer(Chunk);
                 });

You could do the following: 您可以执行以下操作:

var tasks = new List<Task<bool>>();

DataQueue.ToList().ForEach(chunk =>
{
      tasks.Add(Task.Run(() => UploadChunkToServer(chunk)));
});

await tasks.WhenAll();

But you should change UploadChunkToServer to return something; 但是您应该更改UploadChunkToServer以返回某些内容; a bool indicating if the upload was succesful. 一个bool指示上传是否成功。 void Tasks should be avoided, pun intended... void Tasks应该避免void Tasks ,双关语意...

UPDATE: As TheBoyan's answer points out, you can simplify this making UploadChunkToServer async, but I'd still recommend returning something so you can get a hold on the tasks for later processing. 更新:正如TheBoyan的答案所指出的,您可以简化此操作以使UploadChunkToServer异步,但是我仍然建议您返回一些内容,以便保留任务以备后用。 If its a fire and forget scenario, this wouldn't be necessary: 如果发生失火的情况,则没有必要:

var tasks = new List<Task<bool>>();

DataQueue.ToList().ForEach(chunk =>
{
    tasks.Add(UploadChunkToServer(chunk));
});

await tasks.WhenAll();

And UploadChunkToServer would look like: 而且UploadChunkToServer看起来像:

private async Task<bool> UploadChunkToServer(UserCustomFile Chunk)
{
    var client = new RestClient(Helper.GetServerURL());

    var request = new RestRequest("api/fileupload/savechunk", Method.POST);
    request.AddHeader("Content-type", "application/json");
    request.RequestFormat = RestSharp.DataFormat.Json;
    request.AddJsonBody(Chunk);

    return await client.ExecuteAsync(request, response =>
    {
        if (response.StatusCode != System.Net.HttpStatusCode.OK)
        {
            throw new Exception(response.ErrorMessage);
        }
        else
        {
            ChunkStatuses.Add(Chunk.ChunkID);
            return true;
        }
    });
}

Your question is a little unclear, and I think you have mixed up asynchronous and parallel, which are not necessarily the same in C#. 您的问题尚不清楚,我想您将异步和并行混合在一起,在C#中不一定相同。

There is two types of asynchrony in C#: C#中有两种异步类型:

  • I/O bound (read/write over network etc.) 绑定了I / O(通过网络进行读/写等)

  • Long running (high processing etc.) 长时间运行(高处理能力等)

Yours is a I/O bound operation, so it works by notifying the caller when it's done(by providing a state machine in the background). 您的操作是受I / O约束的操作,因此它通过在完成时通知调用方来工作(通过在后台提供状态机)。 Think of it as an event based pattern. 将其视为基于事件的模式。

The way you deal with it best is await if you already have an [YourMethodName]Async method provided by an API or a library, or TaskCompletionSource to implement it yourself. 如果您已经有一个由API或库提供的[YourMethodName] Async方法,或者是一个TaskCompletionSource可以自己实现,则最好的处理方式就是等待。 Note that you don't necessarily need a parallel thread to implement it(by running Task.Run or some other way), the framework (or the one that implemented the method did it for you in the case of [YourMethodName]Async) provides a Task when it's done for you already. 请注意,您不一定需要使用并行线程来实现它(通过运行Task.Run或其他方式),框架(或在[YourMethodName] Async的情况下,实现该方法的框架可以为您提供)已经为您完成的任务。

So, if you want to make your method run client.ExecuteAsync asynchronously assuming ExecuteAsync returns a Task . 因此,如果您想让方法以client.ExecuteAsync异步运行, client.ExecuteAsync假定ExecuteAsync返回Task In your code you can do like this: 在您的代码中,您可以这样做:

// add async keyword to notify that you are awaitng int he method
// perhaps also change the void to Task
private async Task UploadChunkToServer(UserCustomFile Chunk)
{
    var client = new RestClient(Helper.GetServerURL());

    var request = new RestRequest("api/fileupload/savechunk", Method.POST);
    request.AddHeader("Content-type", "application/json");
    request.RequestFormat = RestSharp.DataFormat.Json;
    request.AddJsonBody(Chunk);

    //add await operator to make this part run acync
    return await client.ExecuteAsync(request, response =>
    {
        if (response.StatusCode != System.Net.HttpStatusCode.OK)
        {
            throw new Exception(response.ErrorMessage);
        }
        else
        {
            ChunkStatuses.Add(Chunk.ChunkID);
        }
    });
}

then further up in your call stack: 然后进一步调用堆栈中的内容:

  if (DataQueue.Count == NumberofChunksToRead)
  {
       DataQueue.ToList().ForEach(Chunk =>
       {
           await UploadChunkToServer(Chunk);
       });
  }

You also might want to consider returning a result with Task, so that you can follow the progress or see which ones have already been uploaded etc. 您可能还需要考虑使用Task返回结果,以便您可以跟踪进度或查看已上传的结果等。

If you want to run the method in parallel , the previous answer with Parallel Foreach will do the job. 如果要并行运行该方法,则使用Parallel Foreach的上一个答案将完成此工作。 Although, I think it's not good to do that in this case since you have an I/O bound operation. 虽然,我认为在这种情况下这样做不是很好,因为您具有I / O绑定操作。 What you basically would be doing is running a long running task that does nothing but wait most of the time. 您基本上将要执行的是运行一个长时间运行的任务,该任务除了大部分时间外什么都不做。 In the case of a 4 core processor and with you spawning 4 threads that would mean you occupy all of them by just waiting. 如果使用4核处理器,并且产生了4个线程,则意味着您仅需等待即可占用所有线程。 Something that you are already been doing with await in the first place. 首先,您已经在做等待的事情。

You can't do it because the paramter to ForEach is a diffrent function called by ForEach and not part of your fuction that is async - BUT - the actual solution is to not use ForEach - foreach is actually a bit shorter and doesn't have this problem 您无法执行此操作,因为ForEach的参数是ForEach调用的一个不同函数,而不是异步功能的一部分-但是-实际的解决方案是不使用ForEach-foreach实际上更短并且没有这个问题

if (DataQueue.Count == NumberofChunksToRead)
{
    forach(var Chunk in DataQueue.ToList())
    {
        await UploadChunkToServer(Chunk);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM