Helgrind 在简单的 boost::asio::thread_pool 程序中报告同步错误

Posted

技术标签:

【中文标题】Helgrind 在简单的 boost::asio::thread_pool 程序中报告同步错误【英文标题】:Helgrind reports synchronization errors in simple boost::asio::thread_pool program 【发布时间】:2021-10-17 14:04:19 【问题描述】:

我正在试验 boost::asio::thread_pool 和 helgrind 在一个具有空任务功能的简单程序中报告错误。问题出在哪里,我该如何解决?


#include <boost/thread/mutex.hpp>
#include <boost/thread.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/post.hpp>


int main() 
    ushort thread_num = 4;
    boost::asio::thread_pool pool(thread_num);

    auto task = []() ;

    for (ushort i = 0; i < thread_num; ++i)
        boost::asio::post(pool, task);

    pool.join();

    return 0;

这里是 helmgrind 的输出:

==266706== Thread #1 is the program's root thread
==266706== 
==266706== ----------------------------------------------------------------
==266706== 
==266706== Thread #1: pthread_cond_signal,broadcast: dubious: associated lock is not held by any thread
==266706==    at 0x48405D6: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==266706==    by 0x11508D: bool boost::asio::detail::posix_event::maybe_unlock_and_signal_one<boost::asio::detail::conditionally_enabled_mutex::scoped_lock>(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&) (in /home/arno/Programming/test/a.out)
==266706==    by 0x111AAD: boost::asio::detail::conditionally_enabled_event::maybe_unlock_and_signal_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&) (in /home/arno/Programming/test/a.out)
==266706==    by 0x11361E: boost::asio::detail::scheduler::wake_one_thread_and_unlock(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&) (in /home/arno/Programming/test/a.out)
==266706==    by 0x1132C4: boost::asio::detail::scheduler::post_immediate_completion(boost::asio::detail::scheduler_operation*, bool) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DD99: void boost::asio::thread_pool::executor_type::post<boost::asio::detail::work_dispatcher<main::lambda()#1>, std::allocator<void> >(boost::asio::detail::work_dispatcher<main::lambda()#1>&&, std::allocator<void> const&) const (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DBFB: void boost::asio::detail::initiate_post::operator()<main::lambda()#1&, boost::asio::thread_pool::executor_type const&>(main::lambda()#1&, boost::asio::thread_pool::executor_type const&) const (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DB82: void boost::asio::async_result<main::lambda()#1, void ()>::initiate<boost::asio::detail::initiate_post, lambda()#1&, boost::asio::thread_pool::executor_type const&>(boost::asio::detail::initiate_post&&, lambda()#1&, boost::asio::thread_pool::executor_type const&) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DB54: std::enable_if<void ()::async_result_has_initiate_memfn<main::lambda()#1&, void ()>::value, boost::asio::async_result<std::decay<void ()::async_result_has_initiate_memfn>::type, main::lambda()#1&>::return_type>::type boost::asio::async_initiate<main::lambda()#1&, void (), boost::asio::detail::initiate_post, boost::asio::thread_pool::executor_type const&>(boost::asio::detail::initiate_post&&, void (&)()::async_result_has_initiate_memfn, boost::asio::thread_pool::executor_type const&) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DB12: boost::asio::async_result<std::decay<main::lambda()#1&>::type, void ()>::return_type boost::asio::post<boost::asio::thread_pool::executor_type, main::lambda()#1&>(boost::asio::thread_pool::executor_type const&, std::decay&&, std::enable_if<boost::asio::is_executor<boost::asio::async_result<std::decay<main::lambda()#1&>::type, void ()>::return_type>::value, void>::type*) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DABD: boost::asio::async_result<std::decay<main::lambda()#1&>::type, void ()>::return_type boost::asio::post<boost::asio::thread_pool, main::lambda()#1&>(boost::asio::thread_pool&, std::decay&&, std::enable_if<std::is_convertible<boost::asio::thread_pool, boost::asio::execution_context&>::value, void>::type*) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DA11: main (in /home/arno/Programming/test/a.out)

【问题讨论】:

它在哪里报告数据竞争?只有一条“可疑”的诊断消息,甚至没有表示任何数据争用。 @Ext3h 你说得对,已编辑。 【参考方案1】:

https://linux.die.net/man/3/pthread_cond_signal

无论线程当前是否拥有调用 pthread_cond_wait() 或 pthread_cond_timedwait() 的线程在等待期间与条件变量关联的互斥锁,线程都可以调用 pthread_cond_broadcast() 或 pthread_cond_signal() 函数;但是,如果需要可预测的调度行为,则该互斥锁应由调用 pthread_cond_broadcast() 或 pthread_cond_signal() 的线程锁定。

这只是一个诊断警告,指出了围绕条件变量进行低效调度的常见原因。如果您(作为生产者)在发出信号时不保持互斥锁锁定,您会得到虚假唤醒。例如。可能在您解锁互斥锁后立即处理了您对 CV 变量的更新,因此您的信号随后会导致另一个不必要的唤醒,而没有任何更改状态。

为 CV 变量的整个更新以及相反的信号保持锁定互斥锁可以有效地处理,因为消费者已经在互斥锁和 CV 上成对注册,并且直接向 CV 发送信号会使消费者等待 -列出互斥锁,甚至一次都不激活线程。

这只是效率低下,而不是逻辑错误。 Helgrind 仅将其报告为“可疑”,而不是错误。

【讨论】:

你的意思是这发生在 boost 库中并且我的代码是正确的吗? 是的,警告是由boost库触发的。

以上是关于Helgrind 在简单的 boost::asio::thread_pool 程序中报告同步错误的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个简单的 Boost::asio 程序不能按预期工作?

Boost::asio async_read 简单文件上传

实现真正 boost::asio::async_read_until 的最简单方法

boost::asio之简单客户端服务器回显功能

Boost::Asio : io_service.run() vs poll() 或者我如何在主循环中集成 boost::asio

C++ boost::asio::io_service创建线程池thread_group简单实例