Linux下的AutoResetEvent在c++中的等效函数是什么?

本文关键字:函数 是什么 下的 AutoResetEvent c++ Linux | 更新日期: 2023-09-27 18:14:41

MSDN中AutoResetEvent的描述

我试图在Linux下将c#实现的线程池移植到c++。我不知道我应该使用哪些函数有类似的行为"AutoResetEvent"

Linux下的AutoResetEvent在c++中的等效函数是什么?

AutoResetEvent最类似于二进制信号量。人们说"条件变量"本身并没有错,但条件变量是在类似的情况下使用的,而不是类似的对象。你可以在条件变量的顶部实现一个(未命名的)AutoResetEvent:

#include <pthread.h>
#include <stdio.h>
class AutoResetEvent
{
  public:
  explicit AutoResetEvent(bool initial = false);
  ~AutoResetEvent();
  void Set();
  void Reset();
  bool WaitOne();
  private:
  AutoResetEvent(const AutoResetEvent&);
  AutoResetEvent& operator=(const AutoResetEvent&); // non-copyable
  bool flag_;
  pthread_mutex_t protect_;
  pthread_cond_t signal_;
};
AutoResetEvent::AutoResetEvent(bool initial)
: flag_(initial)
{
  pthread_mutex_init(&protect_, NULL);
  pthread_cond_init(&signal_, NULL);
}
void AutoResetEvent::Set()
{
  pthread_mutex_lock(&protect_);
  flag_ = true;
  pthread_mutex_unlock(&protect_);
  pthread_cond_signal(&signal_);
}
void AutoResetEvent::Reset()
{
  pthread_mutex_lock(&protect_);
  flag_ = false;
  pthread_mutex_unlock(&protect_);
}
bool AutoResetEvent::WaitOne()
{
  pthread_mutex_lock(&protect_);
  while( !flag_ ) // prevent spurious wakeups from doing harm
    pthread_cond_wait(&signal_, &protect_);
  flag_ = false; // waiting resets the flag
  pthread_mutex_unlock(&protect_);
  return true;
}
AutoResetEvent::~AutoResetEvent()
{
  pthread_mutex_destroy(&protect_);
  pthread_cond_destroy(&signal_);
}

AutoResetEvent event;
void *otherthread(void *)
{
  event.WaitOne();
  printf("Hello from other thread!'n");
  return NULL;
}

int main()
{
  pthread_t h;
  pthread_create(&h, NULL, &otherthread, NULL);
  printf("Hello from the first thread'n");
  event.Set();
  pthread_join(h, NULL);
  return 0;
}

但是,如果您需要命名的自动重置事件,则可能需要查看信号量,并且可能在翻译代码时遇到一些困难。无论哪种方式,我都会仔细查看您平台上的pthreads文档,条件变量和自动重置事件不相同,行为也不相同。

我很确定你正在寻找条件变量。另一个SO问题的公认答案是:c#中的条件变量——似乎证实了这一点。

关于POSIX线程中条件变量的详细信息,请参见本教程。

条件变量NOT相当于AutoResetEvent。它们相当于监视器。这个区别很关键,如果使用不当可能会导致死锁:

想象c#程序中的两个线程A和B。A调用WaitOne(), B调用Set()。如果B在A到达WaitOne()调用之前执行Set(),则没有问题,因为Set()发送给AutoResetEvent()的信号是持久的,并且它将保持设置,直到WaitOne()执行。

现在在C中,想象两个线程C和D。C调用wait(), D调用notify()。如果当D调用notify()时,C已经在等待,那么一切都没问题。如果C在D调用notify()之前没有设法到达wait(),则会出现死锁,因为如果没有人在等待信号并且条件变量的状态仍然为"未设置",则信号丢失。

对此要非常小心。

您可以使用POSIX互斥锁和条件变量轻松地重新实现Win32 API事件对象。

然而,上面的一些评论使我不得不这样说:

条件变量不类似于 Event对象。条件变量从根本上不同于事件,因为它没有内存或状态,从某种意义上说,如果在调用pthread_cond_signalpthread_cond_broadcast时没有任何人阻塞条件变量,什么都不会发生,特别是如果线程稍后通过pthread_cond_wait来阻塞,则阻塞。

我将尝试勾画一个快速的自动重置事件实现:

class event
{
public:
  event(): signalled_ (false) {}
  void signal ()
  {
    std::unique_lock<std::mutex> lock(mutex_);
    signalled_ = true;
    cond_.notify_one ();
  }
  void wait ()
  {
    std::unique_lock<std::mutex> lock(mutex_);
    while (!signalled_)
      cond_.wait (lock);
    signalled_ = false;
  }
protected:
  std::mutex mutex_;
  std::condition_variable cond_;
  bool signalled_;
};

Boost的线程/条件文档中的示例与正常的ManualResetEvent和AutoResetEvent的使用非常相似:http://www.boost.org/doc/libs/1_53_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
(为了清晰起见,我做了一些小的编辑)

boost::condition_variable cond;
boost::mutex mut;
bool data_ready;
void wait_for_data_to_process()
{
    boost::unique_lock<boost::mutex> lock(mut);
    while(!data_ready)
    {
        cond.wait(lock);
    } 
}
void prepare_data_for_processing()
{
    {   //scope for lock_guard
        boost::lock_guard<boost::mutex> lock(mut);
        data_ready=true;
    }
    cond.notify_one();
}

请注意,条件提供AutoResetEvent和ManualResetEvent的等待/通知机制,但需要互斥锁才能工作。

嗯,它最像一个互斥锁——你有很多调用者去共享资源,但只允许一个调用者进入。在互斥锁的情况下,调用者会尝试获得互斥锁(例如phtread_mutex_lock),做他们的事情,然后释放(pthread_mutex_unlock),以便其他调用者可以进入。

我知道这可能有点晚了,我没有关于性能差异的信息,但是组合使用pthread_kill和sigwait可能是一个可行的替代方案,如下所示:

在适当的地方声明如下:

int sigin;
sigset_t sigset;

按以下方式初始化前面的变量:

sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigset, null);
在等待线程中,调用sigwait:
sigwait(&sigset, &sigin);

然后,在应该唤醒等待线程的线程上,您可以这样做:

pthread_kill(p_handle, SIGUSR1);

其中p_handle是要解除阻塞的线程的句柄。

这个示例阻塞等待线程,直到SIGUSR1被交付。因为使用了pthread_kill,所以信号只能到达特定的线程。