¿Cuál es la mejor manera de cancelar una tarea que está en estado de bloqueo?
Frecuentes
Visto 1,303 equipos
3
Tengo tareas en ejecución que llaman a un método que lee de RabbitMQ. Cuando no hay nada en la cola, el método simplemente se bloquea. Entonces, las tareas tienen un estado "en ejecución", pero en realidad no están haciendo nada. ¿Hay alguna manera de terminar con gracia estas tareas?
El código que accede a la cola es el siguiente:
private void FindWork(CancellationToken ct)
{
if (ct.IsCancellationRequested)
return;
bool result = false;
bool process = false;
bool queueResult = false;
Work_Work work = null;
try
{
using (Queue workQueue = new Queue(_workQueue))
{
// Look for work on the work queue
workQueue.Open(Queue.Mode.Consume);
work = workQueue.ConsumeWithBlocking<Work_Work>();
// Do some work with the message ...
return;
Las tareas se crean de la siguiente manera:
private void Run()
{
while (!_stop)
{
// Remove and stopped tasks from the pool
List<int> removeThreads = new List<int>();
lock (_tasks)
{
foreach (KeyValuePair<int, Task> task in _tasks)
{
if (task.Value.Status != TaskStatus.Running)
{
task.Value.Wait();
removeThreads.Add(task.Value.Id);
}
}
foreach (int taskID in removeThreads)
_tasks.Remove(taskID);
}
CancellationToken ct = _cts.Token;
TaskFactory factory = new TaskFactory(ct, TaskCreationOptions.LongRunning, TaskContinuationOptions.LongRunning, null);
// Create new tasks if we have room in the pool
while (_tasks.Count < _runningMax)
{
Task task = factory.StartNew(() => FindWork(ct));
lock (_tasks)
_tasks.Add(task.Id, task);
}
// Take a rest so we don't run the CPU to death
Thread.Sleep(1000);
}
}
Actualmente, he cambiado mi código de creación de tareas para que se vea como el siguiente para poder cancelar las tareas. Sé que esta no es una buena solución, pero no sé qué más hacer.
while (_tasks.Count < _runningMax)
{
Task task = factory.StartNew(() =>
{
try
{
using (_cts.Token.Register(Thread.CurrentThread.Abort))
{
FindWork(ct);
}
}
catch (ThreadAbortException)
{
return;
}
}, _cts.Token);
_tasks.Add(task.Id, task);
}
2 Respuestas
1
¿Podría lo siguiente funcionar en su escenario?
En lugar de generar múltiples subprocesos y tenerlos esperando en la cola, tendría un solo subproceso en un ciclo de sondeo infinito y haría que ese generara un nuevo subproceso cuando llega un nuevo trabajo. Puede agregar un semáforo para limitar el número de hilos que creas. Verifique el código de muestra a continuación, he usado BlockingCollection en lugar de RabbitMQ.
public class QueueManager
{
public BlockingCollection<Work> blockingCollection = new BlockingCollection<Work>();
private const int _maxRunningTasks = 3;
static SemaphoreSlim _sem = new SemaphoreSlim(_maxRunningTasks);
public void Queue()
{
blockingCollection.Add(new Work());
}
public void Consume()
{
while (true)
{
Work work = blockingCollection.Take();
_sem.Wait();
Task t = Task.Factory.StartNew(work.DoWork);
}
}
public class Work
{
public void DoWork()
{
Thread.Sleep(5000);
_sem.Release();
Console.WriteLine("Finished work");
}
}
}
y mi clase de prueba
class Test
{
static void Main(string[] args)
{
Consumer c = new Consumer();
Task t = Task.Factory.StartNew(c.Consume);
c.Queue();
c.Queue();
c.Queue();
c.Queue();
c.Queue();
Thread.Sleep(1000);
Console.ReadLine();
}
}
contestado el 29 de mayo de 14 a las 10:05
1
Para que esto funcione, debe cambiar ConsumeWithBlocking
para apoyar la cancelación. No estoy familiarizado con RabbitMQ, pero aparentemente es compatible cancelación en el canal del consumidor.
Entonces, en lugar de hacer Thread.CurrentThread.Abort
del Token.Register
devolución de llamada, haga lo correcto y cancele la operación con gracia a través de la API RabbitMQ adecuada.
En una nota al margen, lo más probable es que el hilo que está tratando de abortar actualmente no sea el que está bloqueado por ConsumeWithBlocking
.
contestado el 28 de mayo de 14 a las 20:05
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas c# multithreading task-parallel-library rabbitmq queueing or haz tu propia pregunta.
¿Puede mostrar el código de cómo está utilizando su sistema de colas? - Scott Chamberlain
En lugar de bloquear el subproceso, ¿no podría aprovechar async/await para que se reanude la ejecución cuando la cola tenga algo, permitiendo así que el subproceso regrese al grupo de subprocesos? - brumScouse
La ejecución se reanuda cuando hay algo en la cola. - Evil August