我有一些库(套接字网络)代码,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都不适合从它们继承。