我有一些库(套接字网络)代码,Task
基于提供了一个基于API的未决请求响应TaskCompletionSource<T>
。但是,TPL中有一个烦人之处在于,似乎不可能阻止同步继续。我会希望能够做的是两种:
- 告诉
TaskCompletionSource<T>
不允许呼叫者附加TaskContinuationOptions.ExecuteSynchronously
,或者 - 设置结果(
SetResult
/TrySetResult
)的方式指定TaskContinuationOptions.ExecuteSynchronously
应忽略的结果,而是使用池
具体来说,我的问题是传入的数据正在由专用的读取器处理,并且如果呼叫者可以附加TaskContinuationOptions.ExecuteSynchronously
它们,则它们可能会使读取器停止运行(这不仅会影响读取器)。以前,我是通过一些黑客来解决此问题的,该黑客可以检测是否存在任何继续,如果将其继续,则将完成推送到上ThreadPool
,但是如果调用者饱和了他们的工作队列,这将产生重大影响,因为不会处理完成及时地。如果他们正在使用Task.Wait()
(或类似方法),则它们实际上将使自己陷入僵局。同样,这就是为什么阅读器使用专用线程而不使用工人的原因。
所以; 在尝试拖累TPL团队之前:我是否缺少选择?
关键点:
- 我不希望外部呼叫者能够劫持我的线程
- 我不能将
ThreadPool
用作实现,因为它在池饱和时需要工作
下面的示例产生输出(顺序可能会随时间而变化):
Continuation on: Main thread
Press [return]
Continuation on: Thread pool
问题在于,一个随机调用者设法在“主线程”上获得了延续。在实际代码中,这会打断主要读者。坏事!
码:
using System;
using System.Threading;
using System.Threading.Tasks;
static class Program
{
static void Identify()
{
var thread = Thread.CurrentThread;
string name = thread.IsThreadPoolThread
? "Thread pool" : thread.Name;
if (string.IsNullOrEmpty(name))
name = "#" + thread.ManagedThreadId;
Console.WriteLine("Continuation on: " + name);
}
static void Main()
{
Thread.CurrentThread.Name = "Main thread";
var source = new TaskCompletionSource<int>();
var task = source.Task;
task.ContinueWith(delegate {
Identify();
});
task.ContinueWith(delegate {
Identify();
}, TaskContinuationOptions.ExecuteSynchronously);
source.TrySetResult(123);
Console.WriteLine("Press [return]");
Console.ReadLine();
}
}
TaskCompletionSource
使用自己的API打包以防止直接调用ContinueWith
,因为TaskCompletionSource
或Task
都不适合从它们继承。