如何序列化异步pthreads代码部分

时间:2018-09-05 17:21:13

标签: multithreading synchronization pthreads semaphore

我有许多抢先(异步)线程(TA),它们希望临时访问共享资源。这将是pthread_mutex的典型用法。

但是,我有几件事使事情变得更加复杂:

  • 我对整个代码几乎没有控制权,因为我只提供了一组函数来简化该过程。

  • 有一组“主”线程(TM),我对此几乎没有控制权,并且使用私有互斥锁来访问所述共享资源。他们使用私有调度程序来管理这些线程,这样就可以随时允许仅一个线程运行,从而有效地使这些线程协同运行。

  • 这些主线程(TM)中的任何一个都可能导致异步线程(TA)运行。这就是我要向其提供额外功能的程序员代码的全部。

我必须等待那些TM线程处于“安全”状态,然后才能允许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()。我该怎么办?

2 个答案:

答案 0 :(得分:2)

当dispatch_semaphore_signal()返回0(表示没有人在等待它)时,它会增加信号量,因此下一个dispatch_semaphore_wait()将获得信号量而无需等待。

您想要的是条件变量,而调度没有提供。可以使用信号量来构造一个。单独保存 有多少可以通过互斥锁唤醒],但是此时,您可能要考虑是否没有在挖一个更深的洞。

分派在设计时就考虑了特定的模型,您似乎正在努力颠覆这一点。也许可以通过另一种方式来获得想要的效果?

答案 1 :(得分:1)

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);
}