You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

收到取消请求后,如何等待所有线程池工作项完成再退出?

问题解答

最佳实现方案

你可以仅通过现有SemaphoreSlim实现需求,无需引入额外同步原语。核心思路是:在收到取消信号后,尝试获取全部初始数量的许可——由于每个工作线程完成时会释放1个许可,只有当所有运行中的工作线程都完成、许可全部回归后,这个操作才会成功,从而实现阻塞等待所有工作项结束的效果。

修改后的代码

using System;
using System.Threading;

namespace ConsoleApp10 {
    class Program {
        static void Main() {
            var cts = new CancellationTokenSource();
            var t = new Thread(Loop);
            t.Start(cts.Token);
            Console.WriteLine("Press any key to cancel.");
            Console.ReadKey();
            Console.WriteLine("Cancelling loop");
            cts.Cancel();
            t.Join();
            Console.WriteLine("At this point all worker threads must have exited.");
            Console.ReadLine();
        }

        static void Loop(object state) {
            var ct = (CancellationToken)state;
            var numMaxThreads = 4;
            var sema = new SemaphoreSlim(numMaxThreads);
            try {
                while (true) {
                    sema.Wait(ct);
                    ThreadPool.QueueUserWorkItem(_ => {
                        try {
                            Work();
                        } finally {
                            sema.Release();
                        }
                    });
                }
            } catch (OperationCanceledException) {
                // 等待所有工作项完成:获取全部初始许可,直到所有已使用的许可被释放
                sema.Wait(numMaxThreads);
                // 释放许可(可选,因sema后续会被回收,但保持语义完整性)
                sema.Release(numMaxThreads);
            }
        }

        static void Work() {
            var r = new Random(Guid.NewGuid().GetHashCode());
            Console.WriteLine($"  {Thread.CurrentThread.ManagedThreadId}: Starting");
            var until = DateTime.Now.AddMilliseconds(1000 + r.Next(5000));
            while (true) {
                var left = until.Subtract(DateTime.Now);
                if (left <= TimeSpan.Zero)
                    break;
                Console.WriteLine($"  {Thread.CurrentThread.ManagedThreadId}: " +
                    $"Working, Left: {left}");
                Thread.Sleep(250 + r.Next(100) * 10);
            }
            Console.WriteLine($"  {Thread.CurrentThread.ManagedThreadId}: Ending");
        }
    }
}

原理说明

  1. 并发限制逻辑:SemaphoreSlim初始有4个许可,每次循环调用sema.Wait(ct)获取1个许可,只有获取成功才会提交工作线程,保证同时运行的工作线程不超过4个。
  2. 取消与等待逻辑:当取消信号触发时,sema.Wait(ct)抛出OperationCanceledException进入catch块。此时调用sema.Wait(numMaxThreads)会阻塞,直到所有工作线程完成并释放许可(许可总数回到4),这就确保了所有当前运行的工作项都已结束。

内容的提问来源于stack exchange,提问作者user22260863

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:27:49