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

Mac下如何用dispatch实现多信号量等待?对应Windows的WaitForMultipleObjects

问题原因与解决方案

原代码的核心问题

你写的Mac端代码采用了无限循环轮询+非阻塞检查信号量的方式,这会导致两个关键问题:

  1. CPU占用过高:线程会一直空转,占用100%的CPU资源,甚至可能抢占其他线程(比如负责post信号量的线程)的调度时间,导致信号量永远无法被触发,看起来就像“一直等待无法结束”。
  2. 低效且不合理:轮询完全没有利用系统的异步等待机制,即使信号量被触发,这种实现方式也属于反模式,不符合平台的最佳实践。

另外,原代码还有语法错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:41:06