Tengo un código de biblioteca (redes de socket) que proporciona una Task
API basada en respuestas pendientes a solicitudes, según TaskCompletionSource<T>
. Sin embargo, hay una molestia en el TPL porque parece imposible evitar las continuaciones sincrónicas. Lo que me gustaría poder hacer es:
- decirle
TaskCompletionSource<T>
que no debe permitir que las personas que llaman se conecten conTaskContinuationOptions.ExecuteSynchronously
, o - establezca el resultado (
SetResult
/TrySetResult
) de una manera que especifique queTaskContinuationOptions.ExecuteSynchronously
debe ignorarse, usando el grupo en su lugar
Específicamente, el problema que tengo es que los datos entrantes están siendo procesados por un lector dedicado, y si una persona que llama puede conectarse TaskContinuationOptions.ExecuteSynchronously
, puede detener al lector (lo que afecta a más que solo a ellos). Anteriormente, he trabajado en torno a esto mediante algún pirata informático que detecta si hay continuaciones presentes y, si lo están, empuja la finalización al ThreadPool
, sin embargo, esto tiene un impacto significativo si la persona que llama ha saturado su cola de trabajo, ya que la finalización no se procesará. en el momento oportuno. Si están usando Task.Wait()
(o similar), entonces esencialmente se bloquearán ellos mismos. Asimismo, esta es la razón por la que el lector está en un hilo dedicado en lugar de utilizar trabajadores.
Entonces; antes de intentar fastidiar al equipo de TPL: ¿me estoy perdiendo una opción?
Puntos clave:
- No quiero que las personas que llaman externas puedan secuestrar mi hilo
- No puedo usar el
ThreadPool
como implementación, ya que debe funcionar cuando el grupo está saturado
El siguiente ejemplo produce resultados (el pedido puede variar según el tiempo):
Continuation on: Main thread
Press [return]
Continuation on: Thread pool
El problema es el hecho de que una persona que llama al azar logró obtener una continuación en "Hilo principal". En el código real, esto interrumpiría al lector principal; ¡cosas malas!
Código:
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
con mi propia API para evitar llamadas directas aContinueWith
, ya que niTaskCompletionSource
, niTask
no se adapta bien a la herencia de ellos.