在非阻塞方式调用TaskCompletionSource.SetResult [英] Calling TaskCompletionSource.SetResult in a non blocking manner
问题描述
我发现 TaskCompletionSource.SetResult();
调用code返回前等待的任务。在我的情况下导致死锁。
这是在一个普通的发开始的简化版本
无效ReceiverRun()
而(真)
{
VAR味精= ReadNextMessage();
TaskCompletionSource<应变及GT;任务请求= [msg.RequestID] 如果(msg.Error == NULL)
task.SetResult(MSG);
其他
task.SetException(新的异常(msg.Error));
}
}
在code的异步的部分看起来像这样。
等待SendAwaitResponse(第一条消息);
SendAwaitResponse(第二个信息)等待()。
等待实际上是嵌套在非异步调用。
该SendAwaitResponse(简体)
公共静态任务<应变及GT; SendAwaitResponse(string信息)
{
变种T =新TaskCompletionSource<应变及GT;();
requests.Add(的getId(MSG),T);
stream.Write(MSG);
返回t.Task;
}
我的假设是,第二SendAwaitResponse将在一个线程池的线程中执行,但它仍然在ReceiverRun创建的线程。
反正有没有设定一个任务的结果,而不继续等待code?
应用程序是一个控制台应用程序
我发现TaskCompletionSource.SetResult();调用code返回前等待的任务。在我的情况下导致死锁。
块引用>是的,我有一个博客文章文档化这(据我所知它不记录在MSDN上)。死锁是因为两件事情:
- 有
的混合物异步
和阻断code(即一个异步
方法被调用等待
)。- 任务延续使用的是定
TaskContinuationOptions.ExecuteSynchronously
。我建议先从最简单可行的解决方案:去除第一件事(1)。即,不混
异步
和等待
调用:等待SendAwaitResponse(第一条消息);
SendAwaitResponse(第二个信息)等待()。相反,使用
等待
一致:等待SendAwaitResponse(第一条消息);
等待SendAwaitResponse(第二个信息);如果你需要,可以
在一个替代点等待
进一步调用堆栈(的未在的异步
法)。这是我最推荐的解决方案。但是,如果您想尝试删除第二件事(2),你可以做一些技巧的,既可以在
任务包住
来迫使它到一个单独的线程(我的<一href=\"http://nitoasyncex.$c$cplex.com/wikipage?title=TaskCompletionSourceExtensions&referringTitle=Documentation\">AsyncEx库有的setResult
。运行* WithBackgroundContinuations
扩展方法是做的正是这一点),或给你的线程实际环境(如我的<一个href=\"http://nitoasyncex.$c$cplex.com/wikipage?title=AsyncContext&referringTitle=Documentation\"><$c$c>AsyncContext$c$c>键入),并指定ConfigureAwait(假)
,这将的cause延续忽略ExecuteSynchronously
标志。但这些解决方案不仅仅是分离复杂得多
异步
和阻断code。作为一个方面说明,看看 TPL数据流;这听起来像你可能会发现它很有用。
I've discovered that
TaskCompletionSource.SetResult();
invokes the code awaiting the task before returning. In my case that result in a deadlock.This is a simplified version that is started in an ordinary
Thread
void ReceiverRun() while (true) { var msg = ReadNextMessage(); TaskCompletionSource<Response> task = requests[msg.RequestID]; if(msg.Error == null) task.SetResult(msg); else task.SetException(new Exception(msg.Error)); } }
The "async" part of the code looks something like this.
await SendAwaitResponse("first message"); SendAwaitResponse("second message").Wait();
The Wait is actually nested inside non-async calls.
The SendAwaitResponse(simplified)
public static Task<Response> SendAwaitResponse(string msg) { var t = new TaskCompletionSource<Response>(); requests.Add(GetID(msg), t); stream.Write(msg); return t.Task; }
My assumption was that the second SendAwaitResponse would execute in a ThreadPool thread but it continues in the thread created for ReceiverRun.
Is there anyway to set the result of a task without continuing its awaited code?
The application is a console application.
解决方案I've discovered that TaskCompletionSource.SetResult(); invokes the code awaiting the task before returning. In my case that result in a deadlock.
Yes, I have a blog post documenting this (AFAIK it's not documented on MSDN). The deadlock happens because of two things:
- There's a mixture of
async
and blocking code (i.e., anasync
method is callingWait
).- Task continuations are scheduled using
TaskContinuationOptions.ExecuteSynchronously
.I recommend starting with the simplest possible solution: removing the first thing (1). I.e., don't mix
async
andWait
calls:await SendAwaitResponse("first message"); SendAwaitResponse("second message").Wait();
Instead, use
await
consistently:await SendAwaitResponse("first message"); await SendAwaitResponse("second message");
If you need to, you can
Wait
at an alternative point further up the call stack (not in anasync
method).That's my most-recommended solution. However, if you want to try removing the second thing (2), you can do a couple of tricks: either wrap the
SetResult
in aTask.Run
to force it onto a separate thread (my AsyncEx library has*WithBackgroundContinuations
extension methods that do exactly this), or give your thread an actual context (such as myAsyncContext
type) and specifyConfigureAwait(false)
, which will cause the continuation to ignore theExecuteSynchronously
flag.But those solutions are much more complex than just separating the
async
and blocking code.As a side note, take a look at TPL Dataflow; it sounds like you may find it useful.
这篇关于在非阻塞方式调用TaskCompletionSource.SetResult的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!