У меня есть некоторый код библиотеки (socket networking), который предоставляет API Task для ожидающих ответов на запросы на основе 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();
}
}