#c #multithreading #locking #mutex #stdlist
Вопрос:
Я пишу программу, у которой есть общая «очередь» строк:
- 2 или 3 потока возвращаются в очередь
- Поток пользовательского интерфейса выводит строку из очереди каждые 0,5 — 1 секунду. (Поток пользовательского интерфейса означает CWnd::OnTimer)
Один из потоков записи очень часто отталкивает строку назад. (Строка на самом деле является журналом, который генерируется по 5-10 строк в секунду.)
Я добавляю следующий кодовый снип об объяснении выше.
class Logger {
std::mutex m_mutex;
std::list<std::string> m_queLogs;
public:
int info(std::string zLog) {
std::lock_guard<std::mutex> lk(m_mutex);
m_queLogs.push_back(std::move(zLog));
...
}
std::string popLog() { //!!!!! Do I need to add a lock_guard here?
if (m_queLogs.size() == 0)
return "";
auto zLog = m_queLogs.front();
m_queLogs.pop_front();
return zLog;
}
}
// Thread 1
int TcpComm::OnRecv(int opCode, char* buf) {
switch (opCode) {
case CODE_LOG: // generated nearly Real-time
Logger::instance()->info(buf);
break;
...
}
...
}
// Thread 2
void MonitorThread(LPVOID lpvoid) {
while (1) {
Sleep(60 * 1000); // generated some times.
Logger::instance()->info(" ---- monitor signal --- ");
...
}
}
// UI Thread
void CLogView::OnTimer(UINT_PTR nIDEvent)
{
// every 500 milisecond.
auto zLog = Logger::instance()->popLog();
CString strLog = convert_utf8_to_cstring(zLog);
m_ctrLogView.AppendString(strLog);
}
Строки m_queLogs
ar никогда не удаляются, только отодвигаются назад в письменных потоках.
Только поток пользовательского интерфейса выводит журнал из m_queLogs
.
Я думаю , что нет никаких проблем без блокировки Logger::popLog()
, но не уверен, что в случае увеличения частоты регистрации.
Пожалуйста, помогите мне принять решение.
Комментарии:
1. Да, вам нужна блокировка по нескольким причинам.
2. Правило таково: если у вас есть общее состояние, и хотя бы один поток записывает это состояние, вам нужна синхронизация.
3. @NathanOliver Если я добавлю блокировку
popLog()
, когда частота регистрации увеличивается, представление журнала застревает и мерцает, а такжеLogger::info
застревает, и программа выходит из строя. Вот почему я хочу удалить блокировкуpopLog()
. Неужели нет никакого решения этой проблемы?
Ответ №1:
ДА. Считыватель должен быть заблокирован, чтобы избежать записи, которая не имеет последовательности по отношению к чтению, что может привести к неопределенному поведению.
Может быть полезно изучить структуру данных неблокирующей очереди. Я думаю, что нескольким писателям нужно заблокировать, но читателя можно заставить ждать бесплатно. Но имейте в виду, что бесплатное ожидание не обязательно означает эффективность.
Комментарии:
1. Если я проверю
std::list::size() > 0
, прежде чем открывать журнал, я думаю, что блокировка не нужна. Что вы думаете, сэр?2. @codingmonster В этом нет необходимости. Вызов
std::list::size
должен быть заблокирован, потому что модуль записи будет обновлять его. Кроме того, теперь, когда я его прочитал, ваш читатель на самом деле тоже писатель, потому что он изменяет список.3. Еще раз извините. В моем случае проверка
std::list::size>0
не очень важна. Если он перейдет в пустое состояние, хотя писатель обновит список доsize>0
этого момента, читатель в следующий раз откроет журнал. Моя цель-просто избежать сбоев, например, исключения 0xc0000005 , а также ускорить обработку . Существует только один читатель, поэтому часть чтения, о которой вы упомянули, может быть проигнорированаstd::list::size>0
. А как насчет этого?