Я пишу многопоточное приложение.
Я использовал классы boost :: interprocess (версия 1.36.0)
По сути, у меня есть рабочие потоки, которых нужно уведомлять, когда им доступна работа.
Я пробовал подходы как «семафор», так и «условие».
В обоих случаях CSwitch (переключение контекста) для рабочих потоков выглядело очень высоким, например, 600 переключений в секунду.
У меня был взгляд на код, и мне показалось, что он просто проверяет флаг (атомарно с использованием мьютекса), а затем возвращает временной интервал, прежде чем повторить попытку в следующий раз.
Я ожидал, что код будет использовать WaitForSingleObject или что-то в этом роде.
По иронии судьбы, именно так я и поступал, прежде чем решил сделать это «правильно» и использовать Boost! (т.е. использование мьютекса для регулярной проверки статуса флага). Единственная разница заключалась в том, что в моем подходе я спал около 50 мс между проверками, поэтому у меня не было проблемы с высоким CSwitch (и да, для меня нормально, что работа не начинается до 50 мс).
Несколько вопросов:
- Имеет ли значение это «высокое» значение CSwitch?
- Произошло бы это, если бы библиотека ускорения использовала CRITICAL_SECTIONS вместо семафоров (меня не волнует межпроцессная синхронизация - все потоки находятся в одном процессе)?
- Произошло бы это, если бы для ускорения использовался WaitForSingleObject?
- Есть ли другой подход в библиотеках 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;
};
Ваше здоровье,
Джон