对于并发队列示例,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 将不会给出任何负面结果。
我有一个来自 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 将不会给出任何负面结果。