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

时间:2018-01-02 11:27:00

标签: c# asynchronous

我有项目列表,对于我想要调用UploadChunkToServer方法的每个项目,但是并行。列表中始终只有4个项目。

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

以上是我在方法中的代码调用,方法签名如下。

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);
        }
    });
}

我试图添加异步运算符但没有运气。有什么想法可以实现吗?

4 个答案:

答案 0 :(得分:2)

也许您可以在具体的情况下使用Parallel和ForEach:

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

答案 1 :(得分:0)

您可以执行以下操作:

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

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

await tasks.WhenAll();

但你应该改变UploadChunkToServer以返回一些东西; bool表示上传是否成功。应该避免void Tasks,双关语...

更新:正如TheBoyan的answer所指出的那样,你可以简化这种制作UploadChunkToServer异步,但我仍然建议你回复一些事情,这样你就能抓住任务供以后处理。如果它是一个火灾和遗忘的场景,这不是必要的:

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

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

await tasks.WhenAll();

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;
        }
    });
}

答案 2 :(得分:0)

您的问题有点不清楚,我认为您混淆了异步和并行,这在C#中不一定相同。

C#中有两种类型的异步

  • I / O绑定(通过网络读/写等)

  • 长时间运行(高处理等)

你的是一个I / O绑定操作,所以它通过在完成后通知调用者(通过在后台提供状态机)来工作。将其视为基于事件的模式。

如果您已经拥有API或库提供的[YourMethodName] Async方法,或者TaskCompletionSource自己实现它,那么您最好处理它的方式就是等待。请注意,您不一定需要并行线程来实现它(通过运行Task.Run或其他方式),框架(或实现该方法的方法在[YourMethodName] Async的情况下为您提供)任务已经为你完成了。

所以,如果你想让你的方法异步运行client.ExecuteAsync,假设ExecuteAsync返回Task。 在您的代码中,您可以这样做:

// 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);
        }
    });
}

然后进入你的调用堆栈:

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

您也可以考虑使用“任务”返回结果,以便您可以关注进度或查看已上传的内容等。

如果您想在并行中运行该方法,则使用 Parallel Foreach 的上一个答案将完成此任务。虽然,我认为在这种情况下执行此操作并不好,因为您有一个I / O绑定操作。你基本上要做的是运行一个长时间运行的任务,除了大部分时间之外什么都不做。在4核心处理器的情况下,你生成4个线程,这意味着你只需等待就可以占用所有这些线程。你一直在做的事情就是在等待。

答案 3 :(得分:-1)

你不能这样做,因为ForEach的参数是ForEach调用的一个不同的函数,而不是异步的功能的一部分 - 但实际的解决方案是不使用ForEach - foreach实际上有点短并没有这个问题

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