是否需要锁定阅读对象?
Posted
技术标签:
【中文标题】是否需要锁定阅读对象?【英文标题】:Is it necessary to lock the reading object? 【发布时间】:2021-09-28 22:27:38 【问题描述】:我正在编写一个具有共享字符串“队列”的程序:
2 或 3 个线程推回队列 UI 线程每隔 0.5 - 1 秒从队列中弹出字符串。 (UI 线程表示 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
的字符串永远不会被删除,只会在写入线程中被推回。
只有 UI 线程会从 m_queLogs
中弹出日志。
我认为不锁定Logger::popLog()
没有问题,但不确定记录频率是否增加。
请帮我做决定。
【问题讨论】:
是的,出于多种原因,您需要一把锁。 规则是:如果你有共享状态,并且至少有一个线程是该状态的写入者,那么你需要同步。 @NathanOliver 如果我在popLog()
上加了一个锁,当记录频率增加时,LogView 会卡住并闪烁,Logger::info
也会卡住并导致程序崩溃。这就是为什么我要删除popLog()
中的锁定。没有办法解决吗?
【参考方案1】:
是的。读取器必须锁定以避免写入与读取相关的未排序的写入,这会导致未定义的行为。
研究非阻塞队列数据结构可能会有好处。我认为多个作者必须锁定,但可以让读者免费等待。但请记住,免费等待并不一定意味着高效。
【讨论】:
如果我在弹出日志之前检查std::list::size() > 0
,我认为锁定是不必要的。先生,您怎么看?
@codingmonster 没必要。对std::list::size
的调用必须被锁定,因为作者会更新它。另外,现在我读了它,你的读者实际上也是一个作家,因为它修改了列表。
对不起,再次。就我而言,检查std::list::size>0
并不是很重要。如果它进入空状态,尽管此时 writer 将列表更新为 size>0
,但 reader 下次会弹出 log。我的目标只是避免崩溃,例如 0xc0000005 异常,同时加快处理速度。只有一个读者,所以你提到的读者的写作部分可以被std::list::size>0
忽略。这个呢?以上是关于是否需要锁定阅读对象?的主要内容,如果未能解决你的问题,请参考以下文章