C# 取消处于阻塞状态的任务的最佳方法是什么?

C# 取消处于阻塞状态的任务的最佳方法是什么?,c#,multithreading,task-parallel-library,rabbitmq,queueing,C#,Multithreading,Task Parallel Library,Rabbitmq,Queueing,我运行的任务调用从RabbitMQ读取的方法。当队列中没有任何内容时,该方法只是阻塞。因此,任务具有“正在运行”状态,但实际上没有执行任何操作。有什么方法可以优雅地结束这些任务吗 访问队列的代码如下所示: private void FindWork(CancellationToken ct) { if (ct.IsCancellationRequested) return; bool result = false;

我运行的任务调用从RabbitMQ读取的方法。当队列中没有任何内容时,该方法只是阻塞。因此,任务具有“正在运行”状态,但实际上没有执行任何操作。有什么方法可以优雅地结束这些任务吗

访问队列的代码如下所示:

 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;
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);
        }
    }
private void FindWork(取消令牌ct)
{
如果(ct.IsCancellationRequested)
返回;
布尔结果=假;
布尔过程=假;
bool-queueResult=false;
Work\u Work=空;
尝试
{
使用(队列工作队列=新队列(_工作队列))
{
//在工作队列中查找工作
打开(Queue.Mode.Consume);
work=workQueue.ConsumeWithBlocking();
//对消息做一些处理。。。
返回;
创建的任务如下所示:

 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;
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);
        }
    }
private void Run()
{
而(!\u停止)
{
//从池中删除并停止任务
List removeThreads=new List();
锁定(_任务)
{
foreach(KeyValuePair任务在_任务中)
{
if(task.Value.Status!=TaskStatus.Running)
{
task.Value.Wait();
removeThreads.Add(task.Value.Id);
}
}
foreach(removeThreads中的int taskID)
_任务。删除(任务ID);
}
取消令牌ct=\u cts.Token;
TaskFactory工厂=新的TaskFactory(ct,TaskCreationOptions.LongRunning,TaskContinuationOptions.LongRunning,null);
//如果池中有空间,则创建新任务
而(_tasks.Count<_runningMax)
{
Task=factory.StartNew(()=>FindWork(ct));
锁定(_任务)
_tasks.Add(task.Id,task);
}
//休息一下,这样我们就不会让CPU死机了
睡眠(1000);
}
}
目前,我已将任务创建代码更改为如下所示,以便可以中止任务。我知道这不是一个好的解决方案,但我不知道还能做什么

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);
            }
while(\u tasks.Count<\u runningMax)
{
任务=工厂。开始新建(()=>
{
尝试
{
使用(_cts.Token.Register(Thread.CurrentThread.Abort))
{
FindWork(ct);
}
}
捕获(线程异常)
{
返回;
}
},_cts.Token);
_tasks.Add(task.Id,task);
}

要实现这一点,您需要更改
ConsumeWithBlocking
以支持取消。我不熟悉RabbitMQ,但显然它支持

因此,不要从
Token.Register
回调中执行
Thread.CurrentThread.Abort
,而是做正确的事情,通过适当的RabbitMQ API优雅地取消操作


另一方面,您当前尝试中止的线程很可能不是被
ConsumeWithBlocking

阻止的线程。以下情况在您的场景中是否可行

与生成多个线程并让它们在队列中等待不同,我将在无限轮询循环中使用一个线程,并在新的工作类型出现时让该线程生成一个新线程。您可以添加信号量来限制创建的线程数。检查下面的示例代码,我使用了BlockingCollection而不是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");
            }
        }
    }

你能展示你如何使用排队系统的代码吗?当队列中有东西时,你不能利用async/Wait让执行恢复,而不是阻塞线程,从而允许线程返回到线程池吗?当队列中有东西时,执行会恢复。