Подтвердить что ты не робот

Как создать потокобезопасные буферы/POD?

Моя проблема довольно распространена, я полагаю, но это сводит меня с ума:

У меня многопоточное приложение с 5 потоками. 4 из этих потоков выполняют свою работу, такую ​​как сетевая связь и доступ к локальной файловой системе, а затем все записывают свой вывод в структуру данных этой формы:

struct Buffer {
  std::vector<std::string> lines;
  bool has_been_modified;
}

Пятый поток печатает эти буферы/структуры на экране:

Buffer buf1, buf2, buf3, buf4;

...

if ( buf1.has_been_modified || 
     buf2.has_been_modified || 
     buf3.has_been_modified || 
     buf4.has_been_modified )
{
  redraw_screen_from_buffers();
}

Как защитить буферы от перезаписи во время чтения или записи на?

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

Спасибо.

4b9b3361

Ответ 1

Вы должны использовать мьютекс. Класс mutex std::mutex. С С++ 11 вы можете использовать std::lock_guard<std::mutex> для инкапсуляции мьютекса с использованием RAII. Таким образом, вы изменили бы структуру Buffer на

struct Buffer {
   std::vector<std::string> lines;
   bool has_been_modified;
   std::mutex mutex;
};

и всякий раз, когда вы читаете или записываете в буфер или has_been_modified, вы будете делать

std::lock_guard<std::mutex> lockGuard(Buffer.mutex); //Do this for each buffer you want to access
... //Access buffer here

и мьютекс будет автоматически освобожден lock_guard, когда он будет уничтожен.

Подробнее о мьютексах здесь.

Ответ 2

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

// Mutex shared between the multiple threads
std::mutex g_BufferMutex;

void redraw_screen_from_buffers()
{
   std::lock_guard<std::mutex> bufferLockGuard(g_BufferMutex);
   //redraw here after mutex has been locked.
}

Затем ваш код модификации буфера должен был бы заблокировать один и тот же мьютекс при изменении буферов.

void updateBuffer()
{
   std::lock_guard<std::mutex> bufferLockGuard(g_BufferMutex);
   // update here after mutex has been locked
}

В этом содержатся некоторые примеры мьютексов.

Ответ 3

То, что вы хотите выполнить, состоит в том, чтобы иметь несколько потоков/рабочих и один наблюдатель. Последний должен выполнять свою работу только тогда, когда все работники выполняются/сигнализируются. Если это так, проверьте код в этом SO q/a. std:: condition_variable - Подождите, пока несколько потоков не уведомят обозревателя

Ответ 4

mutex - очень хорошая вещь, когда вы пытаетесь избежать dataraces, и я уверен, что ответ, отправленный @ Phantom, удовлетворит большинство людей. Однако следует знать, что это не масштабируется для больших систем.

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

Лучшим решением будет использование более сложной структуры, свободной от блокировки. К сожалению, я не думаю, что в STL есть стандартная блокировка. Один пример незанятой очереди доступен здесь

Используя такую ​​структуру, ваши 4 рабочих потока смогут вставлять сообщения в контейнер, в то время как пятый из них будет деактивировать их без каких-либо dataraces

Подробнее о бездонной структуре данных можно найти здесь!