c++ - 使用 std::mutex 实现信号量

标签 c++ multithreading c++11

作为学习练习,我只是尝试使用 std::mutex 和 C++ 标准提供的其他一些东西创建一个 Semaphore 类。我的信号量应根据需要允许尽可能多的 readLock(),但是只有在所有读取都解锁后才能获取 writeLock()

//Semaphore.h
#include <mutex>
#include <condition_variable>

class Semaphore{
public:
    Semaphore();
    void readLock(); //increments the internal counter
    void readUnlock(); //decrements the internal counter
    void writeLock(); //obtains sole ownership.  must wait for count==0 first
    void writeUnlock(); //releases sole ownership.
    int count; //public for debugging
private:
    std::mutex latch;
    std::unique_lock<std::mutex> lk;
    std::condition_variable cv;
};

//Semaphore.cpp
#include "Semaphore.h"
#include <condition_variable>
#include <iostream>

using namespace std;

Semaphore::Semaphore() : lk(latch,std::defer_lock) { count=0; }
void Semaphore::readLock(){ 
    latch.lock();
    ++count;
    latch.unlock();
    cv.notify_all(); //not sure if this needs to be here?
}
void Semaphore::readUnlock(){
    latch.lock();
    --count;
    latch.unlock();
    cv.notify_all(); //not sure if this needs to be here?
}
void Semaphore::writeLock(){
    cv.wait(lk,[this](){ return count==0; }); //why can't std::mutex be used here?
}
void Semaphore::writeUnlock(){ 
    lk.unlock();
    cv.notify_all();
}

我的测试程序会writeLock() 信号量,启动一堆线程,然后释放信号量。紧接着,主线程将再次尝试 writeLock() 信号量。这个想法是,当信号量解锁时,线程将 readLock() 它并阻止主线程执行任何操作,直到它们全部完成。当它们都完成并释放信号量时,主线程可以再次获取访问权限。我知道这不一定会发生,但这是我正在寻找的情况之一。

//Main.cpp
#include <iostream>
#include <thread>
#include "Semaphore.h"

using namespace std;

Semaphore s;

void foo(int n){
    cout << "Thread Start" << endl;
    s.readLock();
    this_thread::sleep_for(chrono::seconds(n));
    cout << "Thread End" << endl;
    s.readUnlock();
}

int main(){
    std::srand(458279);
    cout << "App Launch" << endl;
    thread a(foo,rand()%10),b(foo,rand()%10),c(foo,rand()%10),d(foo,rand()%10);

    s.writeLock();
    cout << "Main has it" << endl;

    a.detach();
    b.detach();
    c.detach();
    d.detach();

    this_thread::sleep_for(chrono::seconds(2));
    cout << "Main released it" << endl;
    s.writeUnlock();

    s.writeLock();
    cout << "Main has it " << s.count << endl;
    this_thread::sleep_for(chrono::seconds(2));
    cout << "Main released it" << endl;
    s.writeUnlock();

    cout << "App End" << endl;

    system("pause"); //windows, sorry
    return 0;
}

程序抛出一个异常,提示“unlock of unowned mutex”。我认为错误在 writeLock()writeUnlock() 中,但我不确定。谁能指出我正确的方向?

编辑:在构造函数中初始化 lk 时缺少 std::defer_lock,但是它没有修复我遇到的错误。如评论中所述,这不是信号量,对于造成的混淆,我深表歉意。重申一下这个问题,这是我得到的输出(括号中的内容只是我的评论,实际上并不在输出中):

App Launch
Thread Start
Thread Start
Main has it
Thread Start
Thread Start
Thread End (what?)
Main released it
f:\dd\vctools\crt_bld\self_x86\crt\src\thr\mutex.c(131): unlock of unowned mutex
Thread End
Thread End
Thread End

最佳答案

这绝对不是“信号量”。

您的 Semaphore 构造函数立即获取了 latch 上的锁,然后您将其解锁两次,因为 writeUnlock() 调用了 lk。 unlock() 和对 writeLock() 的下一次调用尝试等待具有解锁互斥锁的条件变量,这是未定义的行为,然后您对 writeUnlock( ) 尝试解锁未锁定的互斥量,这也是未定义的行为。

你确定构造函数应该立即锁定互斥量吗?我想你想在构造函数中使用 std::defer_lock,然后在 writeLock() 中锁定互斥体。

关于c++ - 使用 std::mutex 实现信号量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18878377/

相关文章:

c# - 如何确保数据流 block 仅按需创建线程?

python - 在未来回调中调用 future 结果时 ThreadPoolExecutor 挂起

c++ - 单引号防止 bindValue 替换占位符标记

c++ - 模板 :Name resolution:Dependent types: -->can any one tell some more examples for this statement?

c++ - 新建/删除运算符重载和基类

c++ - 为什么 std::array 的 operator==() 没有标记为 constexpr?

c++ - 在通用对象更新循环中,按 Controller 更新还是按对象更新更好?

c++ - 在 c 中的二维数组的末尾添加一个额外的数据列

android - Android NDK 的 gettid 和 pthread_self 之间的区别?

c++ - 标记为 noexcept 的函数内部可以有异常吗?