У меня есть код библиотеки (сети сокетов), который предоставляет 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
не хорошо подходят для наследования от них.