如何从任务列表的执行中返回任务
本文关键字:任务 执行 返回 列表 | 更新日期: 2023-09-27 18:26:07
我必须编写一些中间件代码,要求我返回Task对象。我的中间件代码还使用了一个框架,该框架将数据异步发送到连接的客户端,并返回一个Task。
到目前为止,还是挺直接的。
然而,我的用例是我有许多连接的客户端。所以我的问题基本上是,调用每个客户端的异步Send
方法最明智的方法是什么,更重要的是,我应该返回的Task
是什么。我正在使用c#5。
class Client // some framework
{
public Task Send(Object message)
{
// does its thing somehow, returns a Task
}
}
interface Handler<TMessage> // some framework
{
Task Handle(TMessage msg);
}
class Middleware : Handler<Object> // my middleware implementation code
{
List<Client> clients;
public Task Handle(Object msg)
{
//i do some stuff here before calling each client's Send method
return Task.Run(() => clients.ForEach(client => client.Send(msg))); <-- my current solution
}
}
简而言之,我收到一条消息(处理程序),执行一些自定义逻辑(过滤客户端等),然后调用每个客户端异步Send
方法。根据自定义逻辑的性质(因情况而异),我可能也想将该代码封装在Task
中。如有任何意见,我们将不胜感激。
我的困惑是,我应该按原样返回我创建的外部Task
吗?或者有没有其他我不知道的执行每个异步方法的首选方法。
编辑:
Send
方法是异步的,尽管它没有命名为SendAsync
。请记住,这是一个框架调用,所以我不能按照建议将其重命名为SendAsync。但是,请放心,这是一个隐藏的异步调用,并返回一个Task
。
考虑到调用的真正异步性质,我的用法是:
return Task.Run(() => clients.ForEach(client => client.Send(msg)));
如果我不想等待每个客户端的异步Send()方法运行到完成,会导致任何问题吗?
澄清一下:我所说的"引起任何问题",是指简单地执行上述内容是否有任何令人担忧的原因?死锁、未被发现的异常等。。?我之所以这么问,是因为我知道异步编程中有很多微妙之处并不总是显而易见的,比我理解得更好的人可能会指出这是一个可能的问题,或者可能知道实现我目标的更好方法。。。
由于Send()
方法是异步的并返回Task
,因此应该执行以下操作:
public Task Handle(Object msg)
{
return Task.WhenAll(clients.Select(client => client.Send(msg)));
}
WhenAll()
方法将枚举Task
对象的序列(从原始client
值投影),并返回一个Task
对象,该对象表示返回的所有客户端Task
对象的完成。
您应该考虑更改(或鼓励该代码的所有者更改)Send()
方法的名称,使其包含单词Async
(例如SendAsync()
)。显然,编译器并不关心您对该方法的调用,但该约定对于轻松理解基于异步的代码非常有用。
附录:
从你的修订到问题:
如果我不想等待每个客户端的异步Send()方法运行到完成?
如果您不想等待每个客户端的完成,那么只需启动每个操作即可。没有理由使用Task
来启动操作:
public void Handle(Object msg)
{
//i do some stuff here before calling each client's Send method
clients.ForEach(client => client.Send(msg));
}
根据定义,异步方法在完成之前返回。按照惯例,它会很快做到这一点。因此,不需要将ForEach()
的执行封装在Task
中,也不需要Handle()
本身是异步的(至少,对于方法中的这个特定语句来说不是这样)。
我的意思是,简单地执行上述内容有什么值得担心的吗?死锁、未被发现的异常等。。?
当然,您将无法观察到发生的异常,至少基于您迄今为止提供的代码。
至于死锁,没有办法知道您的代码是否会受到这种影响;启动异步操作本身不会创建死锁场景,也不会像您在这里所做的那样"激发并忘记"会增加任何特定的死锁风险(如果有什么不同的话,它可能会降低风险,因为如果您根本没有为每个操作捕获Task
,那么显然就无法同步阻止它们中的任何一个)。