处理多线程执行
答案 0 :(得分:4)
似乎实际的问题应该是:
如何在不按顺序执行的情况下更改AWS Athena中许多客户端的数据库分区?
答案不是ForEachAsync
还是C#8中即将到来的await foreach
。异步循环仍会一次向服务发送一次调用,它“只是”在等待时不会阻塞寻求答案。
在职员工
这是一个并发的工作程序问题,可以使用TPL Dataflow库的ActionBlock类或新的System.Threading.Channel类来处理。
Dataflow库旨在通过在独立块之间移动数据来创建类似于Shell脚本管道的事件/消息处理管道。每个块都在自己的任务/线程上运行,这意味着您只需将处理分解为多个块即可获得并发执行。
在创建块时,还可以增加每个块的处理任务数by specifying the MaxDegreeOfParallelism
option。这使我们可以快速创建可以同时处理大量消息的“工人”。
示例
在这种情况下,“消息”即为Client
。一个ActionBlock可以创建DDL语句 并执行它。每个块都有一个输入队列,这意味着我们可以将消息发布到一个块,然后等待它使用指定的DOP执行所有操作。
我们还可以为队列指定一个限制,这样,如果工作者任务不能足够快地运行,就不会淹没它:
var options=new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = _maxclientsToBeProcessed,
BoundedCapacity = _maxclientsToBeProcessed*3, //Just a guess
});
var block=new ActionBlock<Client>(client=>CreateAndRunDDL(client));
//Post the client requests
foreach(var client in clients)
{
await block.SendAsync(client);
}
//Tell the block we're done
block.Complete();
//Await for all queued messages to finish processing
await block.Completion;
CreateAndRunDDL(Client)
方法应该执行问题循环内的代码。一个好主意是重构它,并创建单独的函数来创建和执行查询,例如:
async Task CreateAndRunDDL(Client client)
{
var query = QueryForClient(...);
LambdaLogger.Log(query);
if (query.Length >= MaxQueryLength) {
throw new Exception("Delete partition query length exceeded.");
}
var queryExecutionId = await StartQueryExecution(query);
await CheckQueryExecutionStatus(queryExecutionId);
}
块也可以链接。如果我们想将多个客户分批处理,我们可以使用BatchBlock
并将其结果输入到我们的操作块中,例如:
var batchClients = new BatchBlock<Client>(20);
var linkOptions = new DataflowLinkOptions
{
PropagateCompletion = true
};
var block=new ActionBlock<Client>(clients=>CreateAndRunDDL(clients));
batchClients.LinkTo(block,linkOptions);
这次,CreateAndRunDDL
方法接受一个Client[]
数组,该数组具有我们在批处理大小中指定的客户/消息数。
async Task CreateAndRunDDL(Client[] clients)
{
var query = QueryForClients(clients);
...
}
现在应该将消息发布到batchClients
块中。完成后,我们需要等待管道中的最后一个块完成:
foreach(var client in clients)
{
await batchClients.SendAsync(client);
}
//Tell the *batch block* we're done
batchClient.Complete();
//Await for all queued messages to finish processing
await block.Completion;