High CSwitch (переключение контекста) при использовании межпроцессного кода Boost (в Windows, Win32)

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

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

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

Я пробовал подходы как «семафор», так и «условие».

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

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

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

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

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

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

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

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

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

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

  ...

  thread.injectWork("hello world");
}

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

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;
};

Ваше здоровье,

Джон


person John    schedule 13.10.2009    source источник
comment
Не могли бы вы показать код, который вы используете в потоках производителя и потребителя (для обоих подходов)?   -  person Éric Malenfant    schedule 13.10.2009
comment
Ага - пример кода добавлен.   -  person John    schedule 14.10.2009


Ответы (2)


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

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

person Éric Malenfant    schedule 15.10.2009
comment
Это здорово - попробую. - person John; 20.10.2009

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

person hackworks    schedule 14.10.2009
comment
Спасибо за предложение. К сожалению, мне нужна поддержка XP. Кроме того, я стараюсь, чтобы кодовая база была переносимой, где это возможно. Спасибо хоть. - person John; 14.10.2009