【问题标题】:C++ threads: cannot unlock mutex in array after condition_variable waitC++线程:条件变量等待后无法解锁数组中的互斥锁
【发布时间】:2016-04-21 09:28:28
【问题描述】:

我正在尝试将一个主线程与 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 中示例中的代码。此示例在wait 之后使用unlock,并且它仅与除主线程(N = 1)之外的一个线程一起工作得很好。但是当适应 N>1 个线程时,就会发生上述错误。

Yam Marcovic 在 cmets 中说我不应该使用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

【问题讨论】:

  • 从 main 中删除 lk[i%4].unlock(),对于初学者来说。
  • 你到底想做什么?在执行结束时,您期望 in_main 变量的结果是什么?
  • >Remove lk[i%4].unlock() from main, for starters 这是给出错误的部分。但为什么?在这个例子中en.cppreference.com/w/cpp/thread/condition_variable你在等待后拥有了锁,所以需要解锁它
  • 这段代码真的很难理解。你能评论一下吗?或者,更好的是,简化它?乍一看,您似乎在两个线程中等待/通知相同的条件变量,还是什么?
  • > 你想做什么?我正在尝试获得一定的打印顺序。 “输出线程,迭代 n”,其中 n % 4 == m,应该与线程 m 的打印交替,“在线程 m,迭代 l”。不同线程的打印之间的顺序无关紧要,只有该线程的打印与主线程的相对顺序。

标签: c++ multithreading stl


【解决方案1】:

您多次尝试unlock 您的互斥锁!仔细看代码:

 for(int i=0; i < N_ITER*N_THREADS; i++) {
        int tid=i % N_THREADS; // Thread id

其中N_ITER 是 10,N_THREADS 始终是 4,因为它们是 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 并且您再次解锁它!在这种情况下会抛出std::system_error

另外,为什么所有这些 C 指针仍然存在?它们中的任何一个都不能随时为空..切换到引用..

此外,通常在处理数组索引时,约定是使用size_t 而不是int

【讨论】:

  • 感谢您的回答。但是:“当 i = 0 时,lk[0] 中的互斥锁被解锁,然后当 i=4 时 tid = 4%4 再次 tid = 0 并且您再次解锁它!”当 i=0 时,主线程将 in_main[0] 设置为 false。当 i=4 时,主线程应该等待 in_main[0] 再次为真。在继续并再次解锁之前,另一个线程应该更改它。另外,我的印象是执行 condition_variable::wait 会给线程一个锁定的锁。 bool修改如果在锁里面真的是undefined吗?
  • 你是什么意思“另一个线程应该改变它”显然它没有
  • 另外,如果你在 in_main 周围设置警卫,那就没问题了。
  • 等待是守卫吗?我链接的例子说是,但显然不是?另外,“显然不是”,显然是这样,请查看示例输出。 (但请注意,我将数组初始化为 false,因此生成的线程开始打印)
  • 抱歉,这确实不外交。我添加了编译和运行时获得的示例输出。它似乎通过修改布尔值将控制权交还给主线程。
【解决方案2】:

我发现了问题所在。这个问题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是在循环内部构造的。

【讨论】:

    猜你喜欢
    • 2020-06-30
    • 2015-07-12
    • 1970-01-01
    • 2021-10-16
    • 2012-06-13
    • 1970-01-01
    • 2012-12-25
    • 2010-11-06
    • 1970-01-01
    相关资源
    最近更新 更多