我正在使用std::/TBP编写一些库代码,用于将lambda (或std::function )的执行上下文“束”到另一个线程,以避免数据竞争(因为std::函数的操作几乎完全与其他线程管理的数据相关)。这基本上是std::promise/std::future的引用示例用法以及显式异常传递,请参阅下面的代码结构。
然而,当使用DRD (来自)进行测试时,我看到了奇怪的数据竞赛报告。根据https://en.cppreference.com/w/cpp/thread/promise/set_value和https://en.cppreference.com/w/cpp/thread/promise/get_future的说法,所有这些都应该很好,不需要任何额外的同步。为了测试目的,我甚至在lock_guard access周围添加了std::mutex和promise/future,结果更加令人困惑,然后我自己的互斥被报告为冲突操作的原因。我尝试了GCC 12和Clang 13,同样的图片。
那么,为什么val研将std::promise/std::future操作标记为不安全,以及如何解决这一问题?有什么方法可以让你做白名单/证明/隐藏吗?
(这是Debian,libstdc++6 12.1.0-5)
==45452== Thread 2:
==45452== Conflicting load by thread 2 at 0x05c64800 size 4
==45452== at 0x4928B55: load (atomic_base.h:488)
==45452== by 0x4928B55: _M_load (atomic_futex.h:86)
==45452== by 0x4928B55: std::__atomic_futex_unsigned<2147483648u>::_M_load_and_test_until(unsigned int, unsigned int, bool, std::memory_order, bool, std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) (atomic_futex.h:113)
==45452== by 0x49287FC: std::__atomic_futex_unsigned<2147483648u>::_M_load_and_test(unsigned int, unsigned int, bool, std::memory_order) (atomic_futex.h:158)
==45452== by 0x4928697: _M_load_when_equal (atomic_futex.h:212)
==45452== by 0x4928697: std::__future_base::_State_baseV2::wait() (future:337)
==45452== by 0x4959849: std::__basic_future<unsigned long>::_M_get_result() const (future:720)
==45452== by 0x4955E55: std::future<unsigned long>::get() (future:806)
==45452== by 0x4954AD5: RunOnIoThread(std::function<unsigned long ()>, unsigned long) (schedlib.cc:335)
...
==45452== Address 0x5c64800 is at offset 32 from 0x5c647e0. Allocation context:
==45452== at 0x484514F: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_drd-amd64-linux.so)
==45452== by 0x4925A4C: std::__new_allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (new_allocator.h:137)
==45452== by 0x49259A0: allocate (allocator.h:183)
==45452== by 0x49259A0: std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) (alloc_traits.h:464)
==45452== by 0x4925840: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&) (allocated_ptr.h:98)
==45452== by 0x4925750: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::__future_base::_State_baseV2, std::allocator<void>>(std::__future_base::_State_baseV2*&, std::_Sp_alloc_shared_tag<std::allocator<void> >) (shared_ptr_base.h:969)
==45452== by 0x49256F6: std::__shared_ptr<std::__future_base::_State_baseV2, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) (shared_ptr_base.h:1712)
==45452== by 0x49256B4: std::shared_ptr<std::__future_base::_State_baseV2>::shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) (shared_ptr.h:464)
==45452== by 0x49255F6: std::shared_ptr<std::__future_base::_State_baseV2> std::make_shared<std::__future_base::_State_baseV2>() (shared_ptr.h:1009)
==45452== by 0x4955D08: std::promise<unsigned long>::promise() (future:1080)
==45452== by 0x49549EB: RunOnIoThread(std::function<unsigned long ()>, unsigned long) (schedlib.cc:307)
...
==45452== Other segment start (thread 1)
==45452== at 0x4854114: pthread_mutex_unlock (in /usr/libexec/valgrind/vgpreload_drd-amd64-linux.so)
==45452== by 0x4914602: __gthread_mutex_unlock(pthread_mutex_t*) (gthr-default.h:779)
==45452== by 0x4916D44: std::mutex::unlock() (std_mutex.h:118)
==45452== by 0x4914807: std::lock_guard<std::mutex>::~lock_guard() (std_mutex.h:235)
==45452== by 0x49546FA: PeekNewJobs(int, short, void*) (schedlib.cc:237)
... // this is basically the job processing on IO thread, taking scheduled tasks and running themMyValues RunOnIoThread(std::function<MyValues()> act, MyValues onRejection = MyValues::INVOCATION_ERROR)
{
if (!act) MyValues::BAD_ARGUMENT;
// if called from IO thread, stay there
if(ThisIsIoThread()) return act();
std::promise<MyValues> pro;
try {
ScheduleOnIoThread([&]() {
try {
auto res = act();
pro.set_value(res);
}
catch (...) { pro.set_exception(std::current_exception()); }
});
}
catch (...) { return onRejection; }
return pro.get_future().get(); // rethrows exception if stored there
}发布于 2022-07-17 18:19:03
对于在一个线程上创建并由两个线程使用的整个whole对象,Val差尔似乎报告了相互冲突的负载。解决办法是对承诺的规范使用:将其传递给线程并忘记它(移动):
std::promise<MyValues> pro;
std::future<MyValues> pro_feature = pro.get_future();
try {
ScheduleOnIoThread([pro = std::move(pro)]() {
try {
auto res = act();
pro.set_value(res);
}
catch (...) { pro.set_exception(std::current_exception()); }
});
}
catch (...) { return onRejection; }
return pro_feature.get();https://stackoverflow.com/questions/73010781
复制相似问题