2012-03-24 36 views
1

所以,我正在使用我的一个类中的调度器。基本上,我们假装一次只能执行一个线程。我们应该使用信号类来允许这些线程阻塞自己以模拟等待CPU的线程。在C++中实现一个二进制信号类

问题是,线程似乎在错误的时间阻止并在错误的时间执行。我想知道我是否缺少对信号量的概念性理解以及如何实现它。我想知道我是否可以对我的实施获得一些反馈。指导员提供了我没有以任何方式修改这个头文件:

class Semaphore { 
private: 
    int    value; 
    pthread_mutex_t m; 
    pthread_cond_t c; 

public: 

    /* -- CONSTRUCTOR/DESTRUCTOR */ 

    Semaphore(int _val); 

    //~Semaphore(); 

    /* -- SEMAPHORE OPERATIONS */ 

    int P(); 
    int V(); 
}; 

这是使用POSIX的东西我的实现:

Semaphore::Semaphore(int _val){ 
    value = _val; 
    c = PTHREAD_COND_INITIALIZER; 
    m = PTHREAD_MUTEX_INITIALIZER; 
} 

int Semaphore::P(){ 
    if(value <= 0){ 
     pthread_cond_wait(&c, &m); 
    } 
    value--; 
} 

int Semaphore::V(){ 
    value++; 
    if(value > 0){ 
     pthread_cond_signal(&c); 
    } 
} 

回答

8

你忽略锁定互斥。其次,你在这里得到的是一个计数信号量,而不是二进制信号量。二进制信号只有两种状态,等等bool变量是合适的:

class Semaphore { 
private: 
    bool   signaled; // <- changed 
    pthread_mutex_t m; 
    pthread_cond_t c; 

    void Lock() { pthread_mutex_lock(&m); }   // <- helper inlines added 
    void Unlock() { pthread_mutex_unlock(&m); } 
public: 

    /* -- CONSTRUCTOR/DESTRUCTOR */ 

    Semaphore(bool); 

    //~Semaphore(); 

    /* -- SEMAPHORE OPERATIONS */ 

    void P(); // changed to void: you don't return anything 
    void V(); 
}; 

默认地将Impl:

// consider using C++ constructor initializer syntax. 

Semaphore::Semaphore(bool s){  // don't use leading underscores on identifiers 
    signaled = s; 
    c = PTHREAD_COND_INITIALIZER; // Not sure you can use the initializers this way! 
    m = PTHREAD_MUTEX_INITIALIZER; // they are for static objects. 

    // pthread_mutex_init(&m); // look, this is shorter! 
} 

void Semaphore::P(){ 
    Lock();    // added 
    while (!signaled){ // this must be a loop, not if! 
     pthread_cond_wait(&c, &m); 
    } 
    signaled = false; 
    Unlock(); 
} 

void Semaphore::V(){ 
    bool previously_signaled; 
    Lock(); 
    previusly_signaled = signaled; 
    signaled = true; 
    Unlock(); // always release the mutex before signaling 
    if (!previously_signaled) 
     pthread_cond_signal(&c); // this may be an expensive kernel op, so don't hold mutex 
} 
+0

“教师提供的这个头文件我没有任何修改”。 – mfontanini 2012-03-24 19:26:42

+0

这可能是这样,但没有必要因此而拒绝我的回答。事实是,你在这里没有二进制信号量,我正确地指出了这一点。也许任务是在不改变头部的情况下将其实现为二进制信号量;这可以做到。 – Kaz 2012-03-24 19:54:15

+0

好的,你走了。 – mfontanini 2012-03-24 20:03:32

3

你计数信号算法缺少while循环,以及不必要的信号旗语。

原始逻辑,用锁中(看对方的回答):

int Semaphore::P(){ 
    Lock(); 
    if(value <= 0){ 
     pthread_cond_wait(&c, &m); 
    } 
    value--; 
    Unlock(); 
} 

int Semaphore::V(){ 
    Lock(); 
    value++; 
    if(value > 0){ 
     pthread_cond_signal(&c); 
    } 
    Unlock(); 
} 

正确的方法:

int Semaphore::P(){ 
    Lock(); 
    while (value <= 0){ // not if 
     pthread_cond_wait(&c, &m); 
    } 
    // value is now > 0, guaranteed by while loop 
    value--; 
    // value is now >= 0 
    Unlock(); 
} 

int Semaphore::V(){ 
    Lock(); 
    int prior_value = value++; 
    Unlock(); 

    // E.g. if prior_value is 50, should we signal? Why? 

    if (prior_value == 0) // was not signaled previously, now is. 
     pthread_cond_signal(&c); 
} 

为了提高效率,收集有关是否将互斥体信号与否的信息,但然后在互斥量外面执行信号。应该尽可能少地使用互斥锁,因为它们增加了争用,降低了并发性。信号操作可能需要数百个周期(到内核的等待队列操作)。

因为可能发生虚假唤醒,您必须在等待条件变量时使用循环。另外,如果你在互斥信号外发出信号,条件信号并不总是进入“预期”线程。在unlocksignal之间,某些线程可以潜入并调用P并递减互斥量。然后,根据病情醒来的那个必须重新评估测试,否则将错误地进行。