2010-03-08 70 views
2

我有以下代码复制Windows手动和自动重置事件。pthread窗口事件等效问题

class event 
{ 
public: 
    event(bool signalled = false, bool ar = true) : 
     _auto(ar), 
     _signalled(signalled) 
    { 
     pthread_mutex_init(&_mutex, NULL); 
     pthread_cond_init(&_cond, NULL); 
    } 

    ~event() 
    { 
     pthread_cond_destroy(&_cond); 
     pthread_mutex_destroy(&_mutex); 
    } 

    void set() 
    { 
     pthread_mutex_lock(&_mutex); 

     // only set and signal if we are unset 
     if (_signalled == false) 
     { 
      _signalled = true; 

      pthread_cond_signal(&_cond); 
     } 

     pthread_mutex_unlock(&_mutex); 
    } 

    void wait() 
    { 
     pthread_mutex_lock(&_mutex); 

     while (_signalled == false) 
     { 
      pthread_cond_wait(&_cond, &_mutex); 
     } 

     // if we're an autoreset event, auto reset 
     if (_auto) 
     { 
      _signalled = false; 
     } 

     pthread_mutex_unlock(&_mutex); 
    } 

    void reset() 
    { 
     pthread_mutex_lock(&_mutex); 

     _signalled = false; 

     pthread_mutex_unlock(&_mutex); 
    } 

private: 
    pthread_mutex_t _mutex; 
    pthread_cond_t _cond; 
    bool _signalled; 
    bool _auto; 
}; 

我的问题围绕“优化”我已经到位的set()方法,其中我只叫pthread_cond_signal()如果事件unsignalled。这是一个有效的优化,还是我通过这样做引入了一些细微的缺陷。

回答

1

如果多个线程正在等待相同的事件,那么由于该“优化”,行为肯定会有所不同。考虑这一系列事件(手动复位模式):

thread 1 - wait 
thread 2 - wait 
thread 3 - wait 
thread 4 - set 
thread 4 - set 
thread 4 - set 
thread 4 - reset 

与您的代码,调用pthread_cond_signal才会被调用一次(疏通线1-3之一);没有优化它将被称为3次(解除所有3个)。

我不知道这是否是一个“缺陷”,因为我不知道你正在模拟的Windows API的精确语义。

+0

这就是非自动重置事件,您可能只需要在重置之前根据多个设置执行1个操作。所以set()对于“优化”实际上更好。通常情况下,条件变量应该表示条件的变化。 – stefaanv 2010-03-08 12:39:24

+0

@David。我同意你的评价,并感谢你的评论。 事件的自动重置特性是这样的,即对已经发送信号的对象的调用集合是无操作的。所以我认为我拥有的代码是可以的,但我可以接受争论。 – ScaryAardvark 2010-03-08 13:21:50

+0

如果在已经设置的手动重置事件上调用set是noop,那么这不是一种优化,它需要正确的行为。否则,在事件再次重置之前调用set会解除阻塞其他线程。 – nos 2010-03-08 18:04:42

-1

我认为_signalled具有挥发性,以防止有关该变量的任何聪明的编译器技巧。

+1

我了解volatile,意味着标记为这样的变量可能会被编译器不可用的代码改变,因此编译器需要注意优化。但是,在使用_signalled的代码中,编译器可以使用所有代码路径,所以我认为任何潜在的优化都是有效的。我当然不能牢牢把握这个动荡的概念。 – ScaryAardvark 2010-03-08 13:14:51

+1

不需要。您的变量由锁保护,volatile在这里没有用处,编译器不允许在pthread_调用中缓存变量。 – nos 2010-03-08 16:41:00

+0

@ScaryAardvark:如果你是对的,那么'挥发性'是不够的。例如,'volatile'并不能保证另一个核心会看到*这个变化。幸运的是,你不正确,而其他机制在代码中使用就足够了。没有足够的“volatile”语义来解决线程间的问题,如果有的话。这就是为什么我们有足够的机制,例如互斥体。 – 2011-09-17 07:10:32

0

对于非自动重置事件,您只能在呼叫set时唤醒一个线程,但会阻止其他线程阻塞。这对我来说似乎并不理智,并造成服务员和服务员之间的竞争状况。