我有一些库(socket networking)代码,它提供了一个基于Task
的API,用于基于TaskCompletionSource<T>
的未决请求响应.然而,TPL中有一个令人烦恼的地方,那就是似乎不可能阻止同步的继续.我能做的是:
- 告诉电话号码为
TaskCompletionSource<T>
的人不要让打电话的人连接TaskContinuationOptions.ExecuteSynchronously
,或者 - 将结果(
SetResult
/TrySetResult
)设置为指定应该忽略TaskContinuationOptions.ExecuteSynchronously
,而是使用池
具体地说,我遇到的问题是,传入的数据是由专用读取器处理的,如果呼叫者可以连接到TaskContinuationOptions.ExecuteSynchronously
,他们就可以拖延读取器(这影响的不仅仅是他们).以前,我曾通过一些黑客攻击解决过这个问题,它检测是否存在any个延续,如果存在,它会将完成推到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();
}
}