如何序列化异步 pthreads 代码段

How to serialize async pthreads code sections

我有许多想要临时访问共享资源的抢占式(异步)线程 (TA)。这将是 pthread_mutex.

的典型用法

但是,我有一些事情让事情变得更复杂:

我必须等待那些 TM 线程处于 "safe" 状态,然后我才能允许 TA 线程访问共享资源。我有办法做到这一点。

因此,我们的想法是挂起(阻塞)所有 TA 线程,直到任一 TM 线程达到安全状态,然后挂起该 TM 线程,允许每个 TA 线程 运行一个接一个,完成后恢复TM线程。

当 TM 线程达到所述安全状态时,将调用我的函数 shared_resource_is_safe()

此外,程序员必须在访问共享资源之前和之后调用我的函数 acquire_access()surrender_access()

所以我有三个函数要实现,我正在努力使用互斥锁和/或信号量来实现我的目标。

这是我到目前为止的想法:

dispatch_semaphore_t semaphore;
pthread_mutex_t mutex;
int is_safe = 0;

void setup_once() {
    semaphore = dispatch_semaphore_create (0);
    pthread_mutex_init (&mutex, NULL);
}

void acquire_access() {
    pthread_mutex_lock (&mutex);
    dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER);
    assert(is_safe);
}

void surrender_access() {
    pthread_mutex_unlock (&mutex);
}

void shared_resource_is_safe() {
    // this shall resume thread that's called acquire_access()
    is_safe = 1;
    while (dispatch_semaphore_signal (semaphore) != 0) {
        // Wait until the signaled thread
        // has called surrender_access()
        pthread_mutex_lock (&mutex);
        pthread_mutex_unlock (&mutex);
    }
    is_safe = 0;
}

信号量用于让任何调用acquire_access()的线程等待shared_resource_is_safe()

互斥体应确保每个异步线程等待 shared_resource_is_safe() 允许它 运行.

不过这并不可靠。我 运行 遇到异步线程中 is_safe 的断言失败的情况,这意味着主线程不等待异步线程调用 surrender_access()。我做错了什么?

当dispatch_semaphore_signal() returns 0(表示没有人等待)时,它已经增加了信号量,因此下一个dispatch_semaphore_wait()将获得信号量无需等待。

你要的是条件变量,dispatch没有提供。可以使用信号量[即。保留一个单独的柜台 有多少人被互斥体保护唤醒]],但在这一点上你可能想考虑一下你是否没有挖一个更深的洞。

Dispatch 在设计时考虑了特定的模型,而您似乎正在努力颠覆它。有没有办法以其他方式获得您想要的效果?

mevets 的回答确定了您的错误。如前所述,您可以使用条件变量解决此问题:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_safe = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_waiters = PTHREAD_COND_INITIALIZER;
int is_safe = 0;
long waiters = 0;

void acquire_access(void)
{   
    pthread_mutex_lock(&mutex);
    waiters++;
    while (!is_safe)
        pthread_cond_wait(&cond_safe, &mutex);
}

void surrender_access(void)
{   
    waiters--;
    if (!waiters)
        pthread_cond_signal(&cond_waiters);
    pthread_mutex_unlock(&mutex);
}

void shared_resource_is_safe(void)
{   
    pthread_mutex_lock(&mutex);
    if (waiters)
    {
        is_safe = 1;
        pthread_cond_broadcast(&cond_safe);
        while (waiters)
            pthread_cond_wait(&cond_waiters, &mutex);
        is_safe = 0;
    }
    pthread_mutex_unlock(&mutex);
}