Mac下如何用dispatch实现多信号量等待?对应Windows的WaitForMultipleObjects
问题原因与解决方案
原代码的核心问题
你写的Mac端代码采用了无限循环轮询+非阻塞检查信号量的方式,这会导致两个关键问题:
- CPU占用过高:线程会一直空转,占用100%的CPU资源,甚至可能抢占其他线程(比如负责post信号量的线程)的调度时间,导致信号量永远无法被触发,看起来就像“一直等待无法结束”。
- 低效且不合理:轮询完全没有利用系统的异步等待机制,即使信号量被触发,这种实现方式也属于反模式,不符合平台的最佳实践。
另外,原代码还有语法错误:for( int = 0; i < semaphoreCnt; ++i ) 里的int后缺少变量名i,这应该是输入时的笔误。
正确实现方案(模拟WaitForMultipleObjects行为)
在macOS平台,应该利用GCD的异步等待机制来实现“等待任意一个信号量触发并返回索引”的需求,避免忙等待。以下是两种可靠的实现方式:
方式一:异步线程分别等待每个信号量
为每个信号量创建独立的异步线程,第一个完成等待的线程会通知主线程并返回索引:
#include <dispatch/dispatch.h> #include <libkern/OSAtomic.h> int waitForAnySemaphore(dispatch_semaphore_t *semaphores, int semaphoreCnt) { __block int triggeredIndex = -1; dispatch_semaphore_t doneSemaphore = dispatch_semaphore_create(0); dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); for (int i = 0; i < semaphoreCnt; ++i) { // 捕获当前循环索引,避免闭包引用问题 int idx = i; dispatch_async(bgQueue, ^{ // 无限等待当前信号量 dispatch_semaphore_wait(semaphores[idx], DISPATCH_TIME_FOREVER); // 原子操作确保只有第一个触发的信号量会设置索引 if (OSAtomicCompareAndSwap32Barrier(-1, idx, &triggeredIndex)) { dispatch_semaphore_signal(doneSemaphore); } }); } // 等待第一个信号量触发 dispatch_semaphore_wait(doneSemaphore, DISPATCH_TIME_FOREVER); // 清理资源 dispatch_release(doneSemaphore); return triggeredIndex; }
方式二:调度源监听信号量状态
利用GCD调度源(dispatch_source_t)监听每个信号量的变化,当信号量被post时触发回调:
#include <dispatch/dispatch.h> int waitForAnySemaphore(dispatch_semaphore_t *semaphores, int semaphoreCnt) { __block int triggeredIndex = -1; dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t *sources = calloc(semaphoreCnt, sizeof(dispatch_source_t)); for (int i = 0; i < semaphoreCnt; ++i) { // 创建信号量类型的调度源 sources[i] = dispatch_source_create(DISPATCH_SOURCE_TYPE_SEMAPHORE, (uintptr_t)semaphores[i], 0, queue); if (!sources[i]) continue; // 设置信号量触发时的回调 dispatch_source_set_event_handler(sources[i], ^{ if (triggeredIndex == -1) { triggeredIndex = i; dispatch_semaphore_signal(waitSemaphore); // 取消所有调度源,避免不必要的监听 for (int j = 0; j < semaphoreCnt; ++j) { if (sources[j]) dispatch_source_cancel(sources[j]); } } }); // 激活调度源 dispatch_resume(sources[i]); } // 等待任意信号量触发 dispatch_semaphore_wait(waitSemaphore, DISPATCH_TIME_FOREVER); // 清理资源 for (int i = 0; i < semaphoreCnt; ++i) { if (sources[i]) dispatch_release(sources[i]); } free(sources); dispatch_release(waitSemaphore); return triggeredIndex; }
这两种方式都能实现和Windows平台WaitForMultipleObjects(cnt, semaphores, false, INFINITE)完全一致的行为:无限等待直到任意一个信号量被触发,返回该信号量的索引,同时将对应信号量的计数减1。
内容的提问来源于stack exchange,提问作者Z0q
相关产品推荐
相关产品推荐

