Что такое эквивалент C ++ для AutoResetEvent под Linux? - PullRequest
9 голосов
/ 15 ноября 2011

Описание AutoResetEvent в MSDN

Я пытаюсь портировать пул потоков, реализованный в C #, на C ++ под Linux.Я не знаю, какие функции, которые я должен использовать, имеют поведение, аналогичное «AutoResetEvent».

Ответы [ 7 ]

11 голосов
/ 15 ноября 2011

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 на вашей платформе, условные переменные и события автоматического сброса не совпадают и не ведут себя одинаково.

3 голосов
/ 15 ноября 2011

Я почти уверен, что вы ищете условные переменные.Принятый ответ на этот другой вопрос SO: Переменные условия в C # - кажется, подтверждает это.

См., Например, это руководство для получения подробной информации о переменных условия в потоках POSIX.

2 голосов
/ 15 ноября 2011

Вы можете легко повторно реализовать объекты событий Win32 API, используя мьютексы POSIX и условные переменные.

Однако некоторые из приведенных выше комментариев заставляют меня заявить следующее:

Переменная условия не аналогична объекту Event. Условная переменная принципиально отличается от События тем, что у нее нет памяти или состояния, в том смысле, что если в момент вызова функции pthread_cond_signal или * никто не был заблокирован в условной переменной 1010 * ничего не произойдет, в частности, если поток приходит позже для блокировки через 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_;
};
1 голос
/ 05 сентября 2013

Пример из документации Boost's 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, но для работы требуется мьютекс.

1 голос
/ 15 ноября 2011

Условные переменные НЕ эквивалент AutoResetEvent.Они являются эквивалентом мониторов.Разница является критической и может вызвать взаимные блокировки, если не используется должным образом:

Представьте себе два потока A и B в программе на C #.A вызывает WaitOne (), а B вызывает Set ().Если B выполняет Set () до того, как A достигает вызова WaitOne (), проблем не возникает, поскольку сигнал, отправляемый в AutoResetEvent () с помощью Set (), является постоянным и будет оставаться установленным до тех пор, пока не будет выполнен WaitOne ().

Теперь в C представим два потока C и D. Вызовы C ожидают (), а вызовы D уведомляют ().Если C уже ждет, когда D вызывает notify (), все в порядке.Если C не удалось достичь wait () до того, как D вызовет notify (), возникнет тупик, потому что сигнал теряется, если его никто не ждет, а состояние условной переменной по-прежнему «не установлено».

Будьте очень осторожны с этим.

0 голосов
/ 13 июня 2019

Я знаю, что это может быть немного поздно для вечеринки, и у меня нет информации о различиях в производительности, но это может быть жизнеспособной альтернативой для использования комбинации 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.

0 голосов
/ 15 ноября 2011

Ну, шансы, что это больше всего похоже на мьютекс - у вас есть несколько вызывающих абонентов, которые используют общий ресурс, но разрешен только один. В случае мьютекса, вызывающие абоненты будут пытаться получить мьютекс (например, phtread_mutex_lock),сделайте свое дело, затем отпустите (pthread_mutex_unlock), чтобы в него мог войти другой абонент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...