Linux 下 AutoResetEvent 的 C++ 等价物是啥?

Posted

技术标签:

【中文标题】Linux 下 AutoResetEvent 的 C++ 等价物是啥?【英文标题】:What is the C++ equivalent for AutoResetEvent under Linux?Linux 下 AutoResetEvent 的 C++ 等价物是什么? 【发布时间】:2011-11-14 21:15:52 【问题描述】:

The description of AutoResetEvent in MSDN

我正在尝试将在 C# 中实现的线程池移植到 Linux 下的 C++。我不知道应该使用哪些功能与“AutoResetEvent”具有相似的行为。

【问题讨论】:

【参考方案1】:

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;

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

【讨论】:

【参考方案2】:

我很确定您正在寻找条件变量。这个其他 SO 问题的公认答案:Condition variables in C# -- 似乎证实了这一点。

参见例如this tutorial 了解 POSIX 线程中条件变量的详细信息。

【讨论】:

Microsoft 喜欢将“库”、“平台”和“语言”之间的区别模糊成一个大混乱,迫使您“购买 Microsoft”。事实上,.Net 中的“AutoResetEvent”类是 Win32 中“Event”原语的包装,类似于 PThreads 中的“条件变量”。学习 Linux 的 PThreads。 PThreads 可以在 Linux 之外的其他平台上使用,也可以在 C++ 之外的其他语言(C、Python 等)上使用。 只是想知道,使用 boost:thread 库实现它会更容易吗? @derekhh:我不知道“更简单”,条件变量是一个相当简单的构造,但如果你已经在使用 boost::thread(IMO 是一个非常好的选择),一定要使用它的条件变量包装器,它会适合你的其余代码:boost.org/doc/libs/1_47_0/doc/html/thread/…【参考方案3】:

条件变量等同于 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(),那么您就会出现死锁,因为如果没有人在等待信号并且条件变量的状态仍然“未设置”,则信号将丢失。

对此要非常小心。

【讨论】:

【参考方案4】:

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

但是上面的一些 cmets 让我这样说:

条件变量与事件对象不相似。条件变量从根本上不同于事件,因为它没有内存或状态,从某种意义上说,如果在您调用 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_;
;

【讨论】:

【参考方案5】:

Boost 的 Thread/Condition 文档中的示例与正常的 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 的等待/通知机制,但需要互斥体才能工作。

【讨论】:

【参考方案6】:

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

【讨论】:

不,它一点也不像互斥锁。它更像是一个条件变量。【参考方案7】:

我知道这可能有点晚了,而且我没有关于性能差异的信息,但使用 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,信号仅到达该特定线程。

【讨论】:

以上是关于Linux 下 AutoResetEvent 的 C++ 等价物是啥?的主要内容,如果未能解决你的问题,请参考以下文章

C#多线程 - 出了啥问题,如何使用 AutoResetEvent

多线程的AutoResetEvent

线程同步之-旋转门AutoResetEvent

AutoResetEvent类的使用

C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEv

可等待的 AutoResetEvent