C ++线程:在condition_variable等待之后无法解锁数组中的互斥锁

时间:2016-04-21 09:28:29

标签: c++ multithreading stl

我正在尝试将一个主线程与N个子线程同步。经过一些阅读后,我使用了condition_variableunique_lock。但是,我总是在OS X中收到错误condition_variable::wait: mutex not locked: Operation not permittedunique_lock::unlock: not locked: Operation not permitted。在Linux中,我只获得Operation not permitted

更清楚:我的目标是获得一系列印刷品:

main thread, passing to 0
thread 0, passing back to main
main thread, passing to 0
thread 0, passing back to main
...

四个线程中的每一个。

我调整了http://en.cppreference.com/w/cpp/thread/condition_variable中示例中的代码。此示例在unlock之后使用wait,并且它只使用除main之外的一个线程(N = 1)。但是当适合使用N> 1线程时,会发生上述错误。

Yam Marcovic在评论中说我不应该使用unlock。但是,为什么cppreference示例使用它呢?为什么它适用于一个主线程和另一个主线程?

以下是代码:

#include <cstdio>
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std;

constexpr int N_THREADS = 4;
constexpr int N_ITER = 10;

bool in_main[N_THREADS] = {false};

void fun(mutex *const mtx, condition_variable *const cv, int tid){
    for(int i=0; i<N_ITER; i++) {
        unique_lock<mutex> lk(*mtx);
        // Wait until in_main[tid] is false
        cv->wait(lk, [=]{return !in_main[tid];});
        // After the wait we own the lock on mtx, which is in lk
        printf("thread %d, passing back to main\n", tid);
        in_main[tid] = true;
        lk.unlock(); // error here, but example uses unlock
        cv->notify_one();
    }
}

int main(int argc, char *argv[]) {
    // We are going to create N_THREADS threads. Create mutexes and
    // condition_variables for all of them.
    mutex mtx[N_THREADS];
    condition_variable cv[N_THREADS];
    thread t[N_THREADS];
    // Create N_THREADS unique_locks for using the condition_variable with each
    // thread
    unique_lock<mutex> lk[N_THREADS];
    for(int i=0; i<N_THREADS; i++) {
        lk[i] = unique_lock<mutex>(mtx[i]);
        // Create the new thread, giving it its thread id, the mutex and the
        // condition_variable,
        t[i] = thread(fun, &mtx[i], &cv[i], i);
    }

    for(int i=0; i < N_ITER*N_THREADS; i++) {
        int tid=i % N_THREADS; // Thread id
        // Wait until in_main[tid] is true
        cv[tid].wait(lk[tid], [=]{return in_main[tid];});
        // After the wait we own the lock on mtx[tid], which is in lk[tid]
        printf("main thread, passing to %d\n", tid);
        in_main[tid] = false;
        lk[tid].unlock(); // error here, but example uses unlock
        cv[tid].notify_one();
    }
    for(int i=0; i<N_THREADS; i++)
        t[i].join();
    return 0;
}

示例输出:

thread 0, passing back to main
main thread, passing to 0
thread 1, passing back to main
thread 0, passing back to main
main thread, passing to 1
thread 2, passing back to main
thread 1, passing back to main
main thread, passing to 2
thread 2, passing back to main
thread 3, passing back to main
main thread, passing to 3
main thread, passing to 0
thread 3, passing back to main
libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: unique_lock::unlock: not locked: Operation not permitted
Abort trap: 6

2 个答案:

答案 0 :(得分:1)

你试图多次qplot(x = p2, y= a3, title="p2 vs a3", size=I(5), geom="point")+scale_x_log10() 你的互斥体!仔细看看代码:

unlock

其中 for(int i=0; i < N_ITER*N_THREADS; i++) { int tid=i % N_THREADS; // Thread id 为10且N_ITER始终为4,因为它们是N_THREADS
我们得到:

constexpr

所以,当 for(int i=0; i < 40; i++) { int tid=i % 4; // Thread id i = 0中的互斥锁被解锁,然后lk[0]然后再i=4再次tid = 4%4并且您再次解锁时!在这种情况下会抛出tid = 0

另外,为什么所有这些C-Pointers呢?它不像任何时候任何一个都可以为null。切换到引用..

另外,通常在处理数组索引时,约定是使用std::system_error而不是size_t

答案 1 :(得分:0)

我发现了问题所在。这个问题Using std::mutex, std::condition_variable and std::unique_lock帮助了我。

构建unique_lock也正在获取unique_lock。所以必须在调用wait之前在循环内完成。函数fun看起来相同,但main现在看起来像这样:

int main(int argc, char *argv[]) {
    // We are going to create N_THREADS threads. Create mutexes and
    // condition_variables for all of them.
    mutex mtx[N_THREADS];
    condition_variable cv[N_THREADS];
    thread t[N_THREADS];
    // Create N_THREADS unique_locks for using the condition_variable with each
    // thread
    for(int i=0; i<N_THREADS; i++) {
        // Create the new thread, giving it its thread id, the mutex and the
        // condition_variable,
        t[i] = thread(fun, &mtx[i], &cv[i], i);
        // DO NOT construct, therefore acquire, a unique_lock
    }

    for(int i=0; i < N_ITER*N_THREADS; i++) {
        int tid=i % N_THREADS; // Thread id
        // Acquire the unique_lock here
        unique_lock<mutex> lk(mtx[tid]);
        // Wait until in_main[tid] is true
        cv[tid].wait(lk, [=]{return in_main[tid];});
        // After the wait we own the lock on mtx[tid], which is in lk[tid]
        printf("main thread, passing to %d\n", tid);
        in_main[tid] = false;
        lk.unlock(); // error here, but example uses unlock
        cv[tid].notify_one();
    }
    for(int i=0; i<N_THREADS; i++)
        t[i].join();
    return 0;
}

唯一的区别是unique_lock是在循环内部构建的。