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

C# 如何从List.ForEach C进行异步调用#,c#,asynchronous,C#,Asynchronous,我有一个项目列表,对于每个项目,我都想调用UploadChunkToServer方法,但要并行。列表中始终只有4项 if (DataQueue.Count == NumberofChunksToRead) { DataQueue.ToList().ForEach(Chunk => { UploadChunkToServer(Chunk); }); } 上面是我在方法中的代码调用,方法签名如下 private void UploadChunkToSe

我有一个项目列表,对于每个项目,我都想调用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);
        }
    });
}

我曾尝试添加异步运算符,但没有成功。您对如何实现这一目标有何想法?

也许您可以具体利用和使用:

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

您可以执行以下操作:

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

DataQueue.ToList().ForEach(chunk =>
{
      tasks.Add(Task.Run(() => 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;
        }
    });
}
private async Task UploadChunkToServer(UserCustomFile Chunk)
{
var client=new RestClient(Helper.GetServerURL());
var request=new RestRequest(“api/fileupload/savechunk”,Method.POST);
AddHeader(“内容类型”、“应用程序/json”);
request.RequestFormat=RestSharp.DataFormat.Json;
AddJsonBody(Chunk);
return wait client.ExecuteAsync(请求、响应=>
{
if(response.StatusCode!=System.Net.HttpStatusCode.OK)
{
抛出新异常(response.ErrorMessage);
}
其他的
{
chunkstatus.Add(Chunk.ChunkID);
返回true;
}
});
}

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

C#中有两种类型的异步

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

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

您的操作是I/O绑定的操作,因此它通过在完成时通知调用方来工作(通过在后台提供状态机)。将其视为基于事件的模式

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

因此,如果要使方法异步运行
client.ExecuteAsync
,则假定ExecuteAsync返回
任务。
在代码中,可以执行以下操作:

// 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个线程,这意味着您只需等待就可以占用所有线程。您已经在做的事情首先需要等待。

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

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

我曾尝试添加异步运算符,但没有运气没有运气意味着什么?编译错误?行为没有什么不同?还有什么?通常,异步调用某个任务与并行执行某个任务不同(在您的情况下,这会导致服务器上的并行化)。并行+IO绑定任务=有点浪费,由于
UploadChunkToServer
返回
void
,因此调用方无法处理
任务,因此可能会带来不便。这是一种可能性(我的解释),OP希望在所有任务完成后,在所有
或类似任务时使用某种类型的
。如果这是一个火灾和遗忘的场景,那么它应该是好的。就像我在回答中说的,它有点宽泛,我不确定OP想要实现什么。但是,如果您想等待调用堆栈中的上层,您可以将UploadChunkToServer更改为返回任务。(更新了答案)
if (DataQueue.Count == NumberofChunksToRead)
{
    forach(var Chunk in DataQueue.ToList())
    {
        await UploadChunkToServer(Chunk);
    }
}