是否需要锁定阅读对象?

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() &gt; 0,我认为锁定是不必要的。先生,您怎么看? @codingmonster 没必要。对std::list::size 的调用必须被锁定,因为作者会更新它。另外,现在我读了它,你的读者实际上也是一个作家,因为它修改了列表。 对不起,再次。就我而言,检查std::list::size&gt;0 并不是很重要。如果它进入空状态,尽管此时 writer 将列表更新为 size&gt;0,但 reader 下次会弹出 log。我的目标只是避免崩溃,例如 0xc0000005 异常,同时加快处理速度。只有一个读者,所以你提到的读者的写作部分可以被std::list::size&gt;0忽略。这个呢?

以上是关于是否需要锁定阅读对象?的主要内容,如果未能解决你的问题,请参考以下文章

是否每个静态方法都需要锁定对象[重复]

如果我们同时从向量中追加和读取数据,我们是不是需要锁定?(无修改)

为啥我们需要在 C# 中锁定和对象?

使用 HSQLDB 的悲观行锁定

EBS查询默认应用用户,比如是否需要锁定修改这些用户

如何在实体框架中完全锁定一行