如何在不成为竞争条件的情况下等待条件?

How do I wait on a condition without it being a race condition?

如果您 运行 按原样编写代码,您可能不会遇到任何问题。问题是如果广播发生在 cond_wait 之前,此代码将不再有效。如果您取消注释下面的睡眠,您将每隔 运行

遇到一次问题

我该如何写才不会出现竞争条件?我碰巧知道我可以使用 futex 解决这个问题,但我正在寻找 pthread 解决方案

//clang++ -g -fsanitize=undefined,thread main.cpp && ./a.out
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t checkpoint = PTHREAD_COND_INITIALIZER;

int data;

void*fn(void*) {
    pthread_mutex_lock(&write_lock);
    auto temp = ++data;
    pthread_mutex_unlock(&write_lock);
    
    //This if makes it so no thread will pass until both/all threads have finished writing to data
    if (temp < 2) {
        //sleep(1); // <--------- uncomment to see problem
        while (temp < 2)
        {
            pthread_mutex_t local_mutex = PTHREAD_MUTEX_INITIALIZER;
            pthread_mutex_lock(&local_mutex);
            pthread_cond_wait(&checkpoint, &local_mutex);

            //Recheck the condition
            pthread_mutex_lock(&write_lock);
            temp = data;
            pthread_mutex_unlock(&write_lock);
        }
    } else {
        pthread_cond_broadcast(&checkpoint);
    }
    
    int sum=data; //no way to get here until both/all threads execute the sync code
    return 0;
}

int main() {
    pthread_t thread_id[2];
    for(int i=0; i<2; i++) {
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_create(&thread_id[i], &attr, &fn, 0);
    }
    for(int i=0; i<2; i++) {
        pthread_join(thread_id[i], 0);
    }
    printf("Finish\n");
}

可能我不清楚你真正的问题。这看起来像是您正在尝试堆叠您的客户端线程,直到 all 它们都超过了特定的检查点。在这种情况下,一旦 data 被所有线程

触发,就会实现该检查点

因此,data 保留您的谓词状态。测试或编写它是互斥锁保护的目的。 变化检测 是 cond-var 的目的。

#include <stdio.h>
#include <stdint.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t checkpoint = PTHREAD_COND_INITIALIZER;

int data;

void *fn(void *pv)
{
    // construction-passed top limit
    intptr_t n = (intptr_t)pv;

    // changing predicate, so lock mutex
    pthread_mutex_lock(&write_lock);
    ++data;

    // changed predicate, so tell people.
    pthread_cond_signal(&checkpoint);

    // mutex is still locked. if our predicate state isn't
    //  sufficient to move on, we wait (and release the mutex
    //  in the process)
    while (data < n)
    {
        pthread_cond_wait(&checkpoint, &write_lock);

        // TODO: woke up here. may be spurious, may be legit, but
        //  we're going to make sure on the next iteration of the
        //  loop by rechecking the predicate data, which we can do
        //  because the mutex is locked coming out of the wait.

        // ALSO: if you want off-the-mutex processing here of some
        //  kind you would do it by releasing the mutex, do your
        //  thread work, then reacquire the mutex again before the
        //  next iteration of the loop
    }

    // NOTE: still own the mutex.
    
    // no way to get here until all threads execute the sync code
    //  note: not a legit use of pthread_self, use at your peril
    printf("%p : %d\n", (void*)pthread_self(), data);

    // leaving the party, release the mtx and tell someone.
    pthread_mutex_unlock(&write_lock);
    pthread_cond_signal(&checkpoint);

    return 0;
}

#define N_THREADS   16

int main()
{
    pthread_t thread_id[N_THREADS];
    for (intptr_t i = 0; i < N_THREADS; i++)
        pthread_create(&thread_id[i], NULL, &fn, (void*)(intptr_t)N_THREADS);

    for (intptr_t i = 0; i < N_THREADS; i++)
        pthread_join(thread_id[i], 0);

    printf("Finish\n");
}

输出(仅示例)

0x7000019e9000 : 16
0x700001448000 : 16
0x700001342000 : 16
0x700001654000 : 16
0x7000016d7000 : 16
0x7000012bf000 : 16
0x70000175a000 : 16
0x7000013c5000 : 16
0x7000017dd000 : 16
0x70000123c000 : 16
0x700001860000 : 16
0x70000154e000 : 16
0x7000018e3000 : 16
0x7000014cb000 : 16
0x700001966000 : 16
0x7000015d1000 : 16
Finish

这就是您 at-least 想要完成的目标。