对于并发队列示例,Helgrind 是否正常工作?

Helgrind is working correct or not for concurrent queue example?

我有一个来自 udemy 的“使用现代 C++ 学习多线程”课程的代码片段。 (这是并发队列演示示例)

#include <queue>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <future>
#include <string>

using namespace std;

template <class T>
class concurrent_queue_cv {
        std::mutex m;
        std::queue<T> q;
        std::condition_variable cv;
public:
        concurrent_queue_cv() = default;
        void push(T value) {
                std::lock_guard<std::mutex> lg(m);
                q.push(value);
                cv.notify_one();
        }

        void pop(T& value) {
                std::unique_lock<std::mutex> lg(m);
                cv.wait(lg, [this] {return !q.empty();});
                value = q.front();
                q.pop();
        }
};

concurrent_queue_cv<string> cq;

// Waiting thread
void reader() {
        string sdata;
        cout << "Reader calling pop..." << endl;
        cq.pop(sdata);                                       // Pop the data off the queue
        cout << "Reader received data: " << sdata << endl;
}

// Modyifing thread
void writer() {
        std::this_thread::sleep_for(2s);                     // Pretend to be busy...
        cout << "Writer calling push..." << endl;
        cq.push("Populated");                                // Push the data onto the queue
        cout << "Writer returned from push..." << endl;
}

int main() {
        cout << "Starting reader" << endl;
        auto r = async(std::launch::async, reader);
        cout << "Starting writer" << endl;
        auto w = async(std::launch::async, writer);
        r.wait();
        w.wait();
}

当我使用 helgrind 运行 这段代码时,我得到了这些。

==1072== 
==1072== Possible data race during write of size 1 at 0x4E03F4E by thread #1
==1072== Locks held: none
==1072==    at 0x48488A6: mempcpy (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1072==    by 0x4B3B661: _IO_new_file_xsputn (fileops.c:1236)
==1072==    by 0x4B3B661: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==1072==    by 0x4B2F3F0: fwrite (iofwrite.c:39)
==1072==    by 0x49E2823: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072==    by 0x49E2BDB: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072==    by 0x10BB05: main (in /home/user/concurentQueue)
==1072== 
==1072== This conflicts with a previous write of size 1 by thread #2
==1072== Locks held: none
==1072==    at 0x48488A6: mempcpy (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1072==    by 0x4B3B661: _IO_new_file_xsputn (fileops.c:1236)
==1072==    by 0x4B3B661: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==1072==    by 0x4B2F3F0: fwrite (iofwrite.c:39)
==1072==    by 0x49E2823: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072==    by 0x49E2BDB: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072==    by 0x10B8CC: reader() (in /home/user/concurentQueue)
==1072==    by 0x1131E3: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /home/user/concurentQueue)
==1072==    by 0x113113: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /home/user/concurentQueue)
==1072==  Address 0x4e03f4e is 14 bytes inside a block of size 1,024 alloc'd
==1072==    at 0x483C893: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1072==    by 0x4B2DD33: _IO_file_doallocate (filedoalloc.c:101)
==1072==    by 0x4B3DEFF: _IO_doallocbuf (genops.c:347)
==1072==    by 0x4B3CF5F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:745)
==1072==    by 0x4B3B6E4: _IO_new_file_xsputn (fileops.c:1244)
==1072==    by 0x4B3B6E4: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==1072==    by 0x4B2F3F0: fwrite (iofwrite.c:39)
==1072==    by 0x49E2823: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072==    by 0x49E2BDB: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072==    by 0x10BAC5: main (in /home/user/concurentQueue)
==1072==  Block was alloc'd by thread #1
==1072== 
--1072-- 
--1072-- used_suppression:     58 helgrind-glibc2X-004 /usr/lib/x86_64-linux-gnu/valgrind/default.supp:949
--1072-- used_suppression:     12 helgrind-glibc2X-005 /usr/lib/x86_64-linux-gnu/valgrind/default.supp:963
==1072== 
==1072== ERROR SUMMARY: 72 errors from 8 contexts (suppressed: 70 from 49)

如果我没记错的话,根据 helgrind,此代码片段可能存在数据竞争。

但是当我使用 -fsanitize=thread 使用 clang 编译它时,它没有显示任何潜在的数据竞争。

clang++ -fsanitize=thread -g -O1 concurentQueue.cpp -lpthread -o concurentQueue

我的问题是我们可以信任 helgrind 吗?或者代码片段是否存在数据竞争?

这些问题是因为 cout。当删除 cout 行时,helgrind 将不会给出任何负面结果。