使用DispatchSemaphore时如何避免线程爆炸问题?
避免DispatchSemaphore引发的线程爆炸问题
你提到的问题确实是用信号量配合全局并发队列时的典型陷阱:一次性把所有任务都派发到全局队列,导致大量线程被创建后阻塞在wait()调用上,既浪费系统线程资源,还会触发线程爆炸(全局队列的线程上限是64)。
下面是两种有效的解决思路:
方案1:用OperationQueue(推荐)
OperationQueue是苹果官方提供的高级任务管理工具,原生支持限制最大并发执行数,它会自动调度任务,只有当有空闲并发名额时才启动新任务,从根源上避免多余线程的创建和阻塞。
代码示例:
// 创建队列并设置最大并发数为5 let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 5 // 封装添加任务的方法 func addTask(operation: @escaping () -> Void) { operationQueue.addOperation(operation) } // 批量添加64个任务 for i in 1...64 { addTask { print("Executing operation \(i)") } }
这种方式不需要手动管理信号量,代码简洁且不易出错,完全符合你的需求——只派发能立即执行的任务,不会产生阻塞的无效线程。
方案2:GCD串行队列+信号量(手动控制派发)
如果一定要基于GCD和信号量实现,核心是不要一次性把所有任务都派发到并发队列,而是用一个串行队列来控制任务的派发流程,只有当信号量有可用名额时,才将任务转派到并发队列执行。
代码示例:
let semaphore = DispatchSemaphore(value: 5) // 用串行队列控制任务派发节奏 let dispatchControlQueue = DispatchQueue(label: "com.task.dispatch.control") // 实际执行任务的并发队列 let workQueue = DispatchQueue.global() func dispatchTask(operation: @escaping () -> Void) { dispatchControlQueue.async { // 在串行队列里等待信号量,确保不会一次性派发所有任务 self.semaphore.wait() workQueue.async { defer { // 任务完成后释放信号量,让下一个任务可以被派发 self.semaphore.signal() } operation() } } } // 添加64个任务 for i in 1...64 { dispatchTask { print("Executing operation \(i)") } }
这里的串行队列会逐个处理任务请求,只有拿到信号量的名额后,才会把任务放到并发队列执行,最多只会有5个线程在工作,不会出现大量线程阻塞的情况。
内容的提问来源于stack exchange,提问作者Ruturaj Nawale
相关产品推荐
相关产品推荐

