У меня есть некоторый код библиотеки (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();
}
}