Высокий CSwitch («переключатель контекста») при использовании межпроцессного кода Boost (в Windows, Win32) - PullRequest
1 голос
/ 13 октября 2009

Я пишу многопоточное приложение.

Я использовал классы boost :: interprocess (версия 1.36.0)

По сути, у меня есть рабочие потоки, о которых нужно уведомлять, когда работа доступна для них.

Я пробовал подходы "семафор" и "условие".

В обоих случаях CSwitch (переключатель контекста) для рабочих потоков казался очень высоким, например, 600 переключателей в секунду.

У меня был гандер в коде, и кажется, что он просто проверяет флаг (атомарно, используя мьютекс), а затем выдает временный интервал перед повторной попыткой в ​​следующий раз.

Я ожидал, что код будет использовать WaitForSingleObject или что-то еще.

По иронии судьбы, именно так я и делал, прежде чем решил сделать это "правильно" и использовать Boost! (т. е. использование мьютекса для регулярной проверки статуса флага). Единственным отличием было то, что в моем подходе я спал примерно 50 мс между проверками, поэтому у меня не было проблемы с высоким переключателем CS (и да, для меня нормально, что работа не начнется до 50 мс).

Несколько вопросов:

  1. Имеет ли значение это "высокое" значение CSwitch?
  2. Это могло бы произойти, если бы библиотека повышения использовала CRITICAL_SECTIONS вместо семафоров (меня не волнует межпроцессная синхронизация - все потоки находятся в одном процессе)?
  3. Произойдет ли это, если boost использовал WaitForSingleObject?
  4. Существует ли другой подход в Boost-библиотеках, использующий вышеупомянутые методы ожидания Win32 (WaitForXXX), который, как я полагаю, не пострадает от этой проблемы CSwitch.

Обновление: Вот пример псевдокода. Я не могу добавить настоящий код, потому что он будет немного сложным. Но это в значительной степени то, что я делаю. Это просто запускает поток для выполнения одноразового асинхронного действия.

ПРИМЕЧАНИЕ: Это только иллюстрации! В этом образце отсутствуют нагрузки, например если вы вызываете injectWork () до того, как поток достигнет «ожидания», он просто не будет работать. Я просто хотел проиллюстрировать свое использование наддува.

Использование выглядит примерно так:

int main(int argc, char** args)
{
  MyWorkerThread thread;
  thread.startThread();

  ...

  thread.injectWork("hello world");
}

Вот пример использования boost.

class MyWorkerThread
{
public:

  /// Do work asynchronously
  void injectWork(string blah)
  {
    this->blah = blah;

    // Notify semaphore
    this->semaphore->post();
  }

  void startThread()
  {
    // Start the thread (Pseudo code)
    CreateThread(threadHelper, this, ...);
  }

private: 


  static void threadHelper(void* param)
  {
    ((MyWorkerThread*)param)->thread();
  }

  /// The thread method
  void thread()
  {
    // Wait for semaphore to be invoked
    semaphore->wait();

    cout << blah << endl;
  }

  string blah;  
  boost::interprocess::interprocess_semaphore* semaphore;
};

А вот и мой "наивный" код опроса:

class MyWorkerThread_NaivePolling
{
public:

  MyWorkerThread_NaivePolling()
  {
    workReady = false;
  }

  /// Do work asynchronously
  void injectWork(string blah)
  {
    section.lock();

    this->blah = blah;
    this->workReady = true;

    section.unlock();
  }

  void startThread()
  {
    // Start the thread (Pseudo code)
    CreateThread(threadHelper, this, ...);
  }

private: 

  /// Uses Win32 CriticalSection
  class MyCriticalSection
  {
    MyCriticalSection();
    void lock();
    void unlock();
  };

  MyCriticalSection section;


  static void threadHelper(void* param)
  {
    ((MyWorkerThread*)param)->thread();
  }

  /// The thread method
  void thread()
  {
    while (true)
    {
      bool myWorkReady = false;
      string myBlah;

      // See if work set 
      section.lock();
      if (this->workReady)
      {
        myWorkReady = true;
        myBlah = this->blah;
      }
      section.unlock();

      if (myWorkReady)
      {
        cout << blah << endl;
        return;
      }
      else
      {
        // No work so sleep for a while
        Sleep(50);
      }
    }
  }

  string blah;  
  bool workReady;
};

Приветствия

John

Ответы [ 2 ]

3 голосов
/ 15 октября 2009

В системах, отличных от POSIX, кажется, что interprocess_condition является эмулированным с использованием цикла, как вы описываете в своем вопросе. И interprocess_semaphore эмулируется с мьютексом и interprocess_condition, так что wait () - заканчивается в том же цикле.

Поскольку вы упоминаете, что вам не нужна межпроцессная синхронизация, вы должны взглянуть на Boost.Thread , который предлагает переносимую реализацию переменных состояния . Забавно, но кажется, что он реализован в Windows " classic ", используя ... семафор.

0 голосов
/ 14 октября 2009

Если вы не возражаете против конкретной Windows (более новые версии для Windows), проверьте ссылку для переменных условий легкого веса CONDITION_VARIABLE (например, критические секции):

...