C++ 已打包的任务挂起在运算符()上
在Ubuntu上使用GCC4.7.2编译,使用C++ 已打包的任务挂起在运算符()上,c++,multithreading,c++11,gcc,C++,Multithreading,C++11,Gcc,在Ubuntu上使用GCC4.7.2编译,使用-std=c++11-O0-pthread编译,我不知何故在代码中创建了一个死锁,似乎它不应该遇到这个问题。我有一个线程,它只是获取了一个锁,然后通过一个向量运行,调用所有东西。同时,主线程将std::packaged_tasks一个接一个地推到它上面,并在该任务的future返回时阻塞。任务本身很琐碎(打印和返回) 这是完整的代码。运行应用程序有时会成功,但几次尝试后就会挂起: #include <iostream> #include
-std=c++11-O0-pthread
编译,我不知何故在代码中创建了一个死锁,似乎它不应该遇到这个问题。我有一个线程,它只是获取了一个锁,然后通过一个向量运行,调用所有东西。同时,主线程将std::packaged_task
s一个接一个地推到它上面,并在该任务的future
返回时阻塞。任务本身很琐碎(打印和返回)
这是完整的代码。运行应用程序有时会成功,但几次尝试后就会挂起:
#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <functional>
std::unique_lock<std::mutex> lock() {
static std::mutex mtx;
return std::unique_lock<std::mutex>{mtx};
}
int main(int argc, char** argv)
{
std::vector<std::function<void()>> messages;
std::atomic<bool> running{true};
std::thread thread = std::thread([&]{
while (running) {
auto lk = lock();
std::cout << "[T] locked with " << messages.size() << " messages." << std::endl;
for (auto& fn: messages) {
fn();
}
messages.clear();
}
});
for (int i = 0; i < 1000000; ++i) {
std::packaged_task<int()> task([=]{
std::cout << "[T] returning " << i << std::endl;
return i;
});
{
auto lk = lock();
messages.emplace_back(std::ref(task));
}
task.get_future().get();
}
running = false;
thread.join();
}
发生什么事了?为什么对packated\u task::operator()的调用挂起?僵局在哪里?这是一个gcc错误吗
[更新]死锁时,两个线程位于:
线程1(第39行是任务。get_future().get()
行):
我无法解释为什么您的代码被破坏,但我找到了修复它的方法(存储任务,而不是从任务构造的std::functions
):
#包括
#包括
#包括
#包括
#包括
#包括
int main(int argc,字符**argv)
{
//让我们面对现实吧-你的lock()函数有点奇怪。
std::互斥mtx;
//我把它从一个向量变成了一个任务向量
//功能。似乎已经完成了工作。不太确定
//但这似乎是正确的做法。
std::向量消息;
std::原子运行{true};
std::thread thread([&]{
(跑步时){
std::unique_lock l{mtx};
std::cout问题似乎在于,在工作线程中返回操作符()之前,可能会销毁打包的_任务
。这很可能是未定义的行为。如果我在等待将来返回结果后在循环中重新获取互斥体,则该程序对我来说运行良好。这序列化了操作符()
和打包_任务的析构函数。@AlexeyKukanov我在看到这篇文章时就想到了这一点,但我没有看到链接。这篇文章中没有类似的生存期问题,对吧,我可以很容易地用g++4.7重现锁定,但该程序在g++4.8和g++4.9上运行良好。这似乎是编译器或库的问题。我只是alground使用gcc 4.7编译的版本,这确实是一个终身问题:我可以在任务已被销毁的情况下写入“once”指示符。尝试再次使用4.9.1进行复制。我再次使用gcc 4.9.1进行复制,并使用已取消竞争的源代码。现在主线程挂起锁定唯一锁(在下一次迭代中)当worker线程挂起广播destromed condition变量时。现在应该在GCC svn中修复,如果您取消锁定函数,不妨使用lock\u guard
:)我认为您的代码仍然存在问题,但不知怎的,它似乎可以工作(我认为它是UB,请参阅我的新答案)template class packaged_task
是一个部分专门化任务。std::packaged_task
@LightnessRacesinOrbit部分专门化匹配将R
推断为int
并将ArgTypes
推断为空包。@LightnessRacesinOrbit该“问题”我的报道是一个误读。很抱歉把你们都弄糊涂了。请随意忘记这一集:只是一个旁注:我在boost::barrier(boost 1.53)上遇到了类似的问题在Visual Studio 2010上。仅让主线程在使用屏障后继续运行以确保从屏障返回的所有其他线程::等待并销毁屏障对象是正常的是不够的。注意:一个pthread_cond_t
被显式允许在调用pthread_cond_broadcast
唤醒所有等待者(即使他们在事件发生后才真正醒来)。本例中的问题是,我在pthread\u cond\u广播之前解锁互斥锁,这会留下一个小窗口,等待线程可以看到共享状态变为就绪并销毁任务(及其包含condvar的共享状态)在我发出condvar信号之前。@JonathanWakely谢谢大家!用future.get();
替换为future.wait();lock();return future.get();
为我修复了它-谢谢你们查看它并提供了建议。我会随时更新编译器。。。
[T] returning 127189
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 1 messages.
[T] returning 127190
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 1 messages.
[T] returning 127191
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 0 messages.
[T] locked with 1 messages.
... hangs forever ...
#0 pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:162
#1 0x00007feb01fe800c in __gthread_cond_wait (this=Unhandled dwarf expression opcode 0xf3
)
at [snip]/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:879
#2 std::condition_variable::wait (this=Unhandled dwarf expression opcode 0xf3
) at [snip]/gcc-4.7.2/libstdc++-v3/src/c++11/condition_variable.cc:52
#3 0x0000000000404aff in void std::condition_variable::wait<std::__future_base::_State_base::wait()::{lambda()#1}>(std::unique_lock<std::mutex>&, std::__future_base::_State_base::wait()::{lambda()#1}) (this=0x6111e0, __lock=..., __p=...)
at [snip]gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/condition_variable:93
#4 0x0000000000404442 in std::__future_base::_State_base::wait (this=0x6111a8)
at [snip]gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/future:331
#5 0x00000000004060fb in std::__basic_future<int>::_M_get_result (this=0x7fffc451daa0)
at [snip]gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/future:601
#6 0x0000000000405488 in std::future<int>::get (this=0x7fffc451daa0)
at [snip]gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/future:680
#7 0x00000000004024dc in main (argc=1, argv=0x7fffc451dbb8) at test.cxx:39
#0 pthread_once () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:95
#1 0x00000000004020f6 in __gthread_once (__once=0x611214, __func=0x401e68 <__once_proxy@plt>)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/x86_64-unknown-linux-gnu/bits/gthr-default.h:718
#2 0x0000000000404db1 in void std::call_once<void (std::__future_base::_State_base::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()()>&, bool&), std::__future_base::_State_base* const, std::reference_wrapper<std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()()> >, std::reference_wrapper<bool> >(std::once_flag&, void (std::__future_base::_State_base::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()()>&, bool&), std::__future_base::_State_base* const&&, std::reference_wrapper<std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()()> >&&, std::reference_wrapper<bool>&&) (__once=..., __f=@0x7feb014fdc10)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/mutex:819
#3 0x0000000000404517 in std::__future_base::_State_base::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()()>, bool) (this=0x6111a8, __res=..., __ignore_failure=false)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/future:362
#4 0x0000000000407af0 in std::__future_base::_Task_state<int ()()>::_M_run() (this=0x6111a8)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/future:1271
#5 0x00000000004076cc in std::packaged_task<int ()()>::operator()() (this=0x7fffc451da30)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/future:1379
#6 0x000000000040745a in std::_Function_handler<void ()(), std::reference_wrapper<std::packaged_task<int ()()> > >::_M_invoke(std::_Any_data const&) (
__functor=...) at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:1956
#7 0x00000000004051f2 in std::function<void ()()>::operator()() const (this=0x611290)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:2311
#8 0x000000000040232f in operator() (__closure=0x611040) at test.cxx:22
#9 0x0000000000403d8e in _M_invoke<> (this=0x611040)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:1598
#10 0x0000000000403cdb in operator() (this=0x611040)
at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:1586
#11 0x0000000000403c74 in _M_run (this=0x611028) at [snip]/gcc-4.7.2/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/thread:115
#12 0x00007feb01feae10 in execute_native_thread_routine (__p=Unhandled dwarf expression opcode 0xf3
) at [snip]/gcc-4.7.2/libstdc++-v3/src/c++11/thread.cc:73
#13 0x00007feb018879ca in start_thread (arg=<value optimized out>) at pthread_create.c:300
#14 0x00007feb015e569d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#15 0x0000000000000000 in ?? ()
#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <functional>
#include <unistd.h>
int main(int argc, char** argv)
{
// Let's face it - your lock() function was kinda weird.
std::mutex mtx;
// I've changed this to a vector of tasks, from a vector
// of functions. Seems to have done the job. Not sure exactly
// why but this seems to be the proper way to go.
std::vector<std::packaged_task<int()>> messages;
std::atomic<bool> running{true};
std::thread thread([&]{
while (running) {
std::unique_lock<std::mutex> l{mtx};
std::cout << "[T] locked with " << messages.size() << " messages." << std::endl;
for (auto& fn: messages) {
fn();
}
messages.clear();
}
});
for (int i = 0; i < 1000000; ++i) {
std::packaged_task<int()> task([i]{
std::cout << "[T] returning " << i << std::endl;
return i;
});
// Without grabbing this now, if the thread executed fn()
// before I do f.get() below, it complained about having
// no shared state.
std::future<int> f = task.get_future();
{
std::unique_lock<std::mutex> l{mtx};
messages.emplace_back(std::move(task));
}
f.get();
}
running = false;
thread.join();
}