2013-09-18 2 views
4

Как упражнение для обучения, я просто пытаюсь сделать класс Semaphore, используя std::mutex и несколько других вещей, предоставляемых стандартом C++. Мой семафор должен при необходимости использовать столько readLock(), но writeLock() может быть приобретен только после того, как все чтения будут разблокированы.Реализация Семафора с std :: mutex

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

Программа выдает исключение, в котором говорится: «Разблокировка неумелого мьютекса». Я думаю, что ошибка находится в writeLock() или writeUnlock(), но я не уверен. Может кто-то указать мне верное направление?

EDIT: при инициализации 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 
+6

Похоже, вы описываете «блокировку читателя-писателя», а не более общий «семафор». –

+0

@CoryNelson Я думаю, что вы правы, но есть ли тонкий намек на решение из этого комментария, который мне не хватает, или это просто семантика? – Suedocode

+1

Решение проблемы. Вы хотите реализовать семафор или блокировку чтения-записи? –

ответ

3

Это, безусловно, не «семафор».

Ваш Semaphore конструктор получает блокировку на latch сразу, то разблокировать его два раза, потому что writeUnlock() звонки lk.unlock() и следующий вызов writeLock() пытается ждать на условной переменной с разблокированным мьютекса, что неопределенное поведение, то ты следующий вызов writeUnlock() пытается разблокировать разблокированный мьютекс, что также является неопределенным поведением.

Вы уверены, что конструктор должен немедленно заблокировать мьютексы? Я думаю, вы хотите использовать std::defer_lock в конструкторе, а затем заблокировать мьютексы в writeLock().

+0

Ahh да, у моего исходного кода был 'std :: defer_lock', но я не знаю, почему он не попал в код вопроса. Сейчас я отредактирую это. – Suedocode

+0

Этого недостаточно, чтобы исправить это, теперь первый вызов 'writeLock()' будет пытаться подождать с использованием мьютекса, который не принадлежит, и первый вызов 'writeUnlock()' будет неопределенным поведением. Когда вы вызываете 'cv.wait()' первое, что он делает, это разблокировать мьютекс, поэтому он должен быть заблокирован перед вызовом! –

+0

OOOHHHH derp, который исправил его! Большое спасибо. – Suedocode

Смежные вопросы