avo*_*avo 4 .net c# task task-parallel-library async-await
难道TaskScheduler.Default 不是总能保证该任务将在一个池中的线程来执行?
在修复错误时,我发现至少有一个案例没有.它可以像这样再现(一个由真实代码制作的人为例子):
var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
sc.Post(_ => tcs.SetResult(true), null);
await tcs.Task.ContinueWith(_ =>
{
// breaks here
Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
Run Code Online (Sandbox Code Playgroud)
还有其他案件吗?
此外,是否有一种优雅的方法来确保ContinueWith操作是同步执行的,如果先前的任务已在池线程上完成,或者排队等待线程池(我知道我可以使用QueueUserWorkItem它ContinueWith,但我不喜欢它) ).
编辑,我想我可以实现自己的,TaskScheduler并检查我是否已经在线程池线程内TryExecuteTaskInline,以控制它.
我认为发生这种情况的原因是因为你使用了TaskContinuationOptions.ExecuteSynchronously.来自doco:
ExecuteSynchronously指定应同步执行continuation任务.如果指定了此选项,则将在导致先行任务转换到其最终状态的同一线程上运行continuation.如果在创建延续时前提已经完成,则继续将在创建延续的线程上运行.只有非常短暂的延续才能同步执行.
如果先前任务在线程池线程以外的线程上完成,那么继续也将在该线程上运行.所以我想在那种情况下不会发生任何调度.另一种情况可能是延续任务已经完成,也将同步运行.
更新
为了达到你在问题的第二部分所要求的内容,我认为你需要一个定制的等待者.有关更多详细信息,请参阅此内容,但这样的内容可能对您有用:
public static class Extensions
{
public static ThreadPoolTaskAwaiter WithThreadPool(this Task task)
{
return new ThreadPoolTaskAwaiter(task);
}
public class ThreadPoolTaskAwaiter : INotifyCompletion
{
private readonly TaskAwaiter m_awaiter;
public ThreadPoolTaskAwaiter(Task task)
{
if (task == null) throw new ArgumentNullException("task");
m_awaiter = task.GetAwaiter();
}
public ThreadPoolTaskAwaiter GetAwaiter() { return this; }
public bool IsCompleted { get { return m_awaiter.IsCompleted; } }
public void OnCompleted(Action continuation)
{
if (Thread.CurrentThread.IsThreadPoolThread)
{
continuation();
}
else
{
Task.Run(continuation);
}
}
public void GetResult()
{
m_awaiter.GetResult();
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你像这样使用它:
public static async Task Execute()
{
await Task.Delay(500).WithThreadPool();
// does not break here
if (!Thread.CurrentThread.IsThreadPoolThread)
{
Debugger.Break();
}
}
Run Code Online (Sandbox Code Playgroud)