pthread_once()中的竞争条件? [英] race-condition in pthread_once()?

查看:225
本文介绍了pthread_once()中的竞争条件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一个线程中有一个 std :: future 正在等待 std :: promise 在另一个线程。



编辑:更新了一个将永久阻止的示例应用程序的问题:



UPDATE:如果我改用 pthread_barrier ,以下代码会



我创建了一个测试应用程序来说明这一点:



> foo 创建一个线程,该线程在其运行函数中设置 promise promise 的构造函数设置。一旦设置,它增加原子计数



然后我创建了一组 foo 对象,将其删除,然后检查我的计数

  #include< iostream> 
#include< thread>
#include< atomic>
#include< future>
#include< list>
#include< unistd.h>

struct foo
{
foo(std :: atomic< int>& count)
:_stop(false)
{
std :: promise< void> p;
std :: future< void> f = p.get_future();

_thread = std :: move(std :: thread(std :: bind(& foo :: run,this,std :: ref(p))));

//阻塞调用者,直到我的线程启动
f.wait();

++ count; //我的线程已经开始,增加计数
}
void run(std :: promise< void>& p)
{
p.set_value //线程已经启动,唤醒未来

while(!_stop)
sleep(1);
}
std :: thread _thread;
bool _stop;
};

int main(int argc,char * argv [])
{
if(argc!= 2)
{
std :: cerr< ;& usage:< argv [0]<< num_threads< std :: endl;
return 1;
}
int num_threads = atoi(argv [1]);
std :: list< foo *>线程;
std :: atomic< int> count(0); // count will be inc'd once per thread

std :: cout<< 创建线程< std :: endl;
for(int i = 0; i threads.push_back(new foo(count));

std :: cout<< 停止螺纹< std :: endl;
for(auto f:threads)
f-> _stop = true;

std :: cout<< 接合线 std :: endl;
for(auto f:threads)
{
if(f-> _thread.joinable())
f-> _thread.join
}

std :: cout<< count =< count<< (num_threads == count?pass:fail!)<< std :: endl;
return(num_threads == count);
}

如果我在一个循环中运行1000个线程,它几次,直到比赛发生,并且 futures 之一从未被唤醒,因此该应用程序永远卡住。

 #这个循环永远不会在{1..1000}中完成
$ for i; do./a.out 1000; done

如果我现在 SIGABRT 结果堆栈跟踪显示它卡在 future :: wait
堆栈跟踪如下:

  // main thread 
pthread_cond_wait @@ GLIBC_2.3.2()from /lib64/libpthread.so.0
__gthread_cond_wait(__mutex =< optimized out> ;, __cond =< optimized out>)at libstdc ++ - v3 / include / x86_64-unknown-linux-gnu / bits / gthr-default.h:846
std :: condition_variable :: wait(this =< optimized out& ,__lock = ...)at ../../../../libstdc++-v3/src/condition_variable.cc:56
std :: condition_variable :: wait< std :: __ future_base :: _ St​​ate_base :: wait():: {lambda()#1}>(std :: unique_lock< std :: mutex>& std :: __ future_base :: _ St​​ate_base :: wait }($ = 0x93a050,__lock = ...,__p = ...)at include / c ++ / 4.7.0 / condition_variable:93
std :: __ future_base :: _ St​​ate_base :: wait(this = 0x93a018)在include / c ++ / 4.7.0 / future:576
foo :: wait(this = 0x7fff32587870)at include / c ++ / 4.7.0 / future:331
std :: __ basic_future< main.cpp中的foo(this = 0x938320,count = ...)at main.cpp:18
main(argc = 2,argv = 0x7fff32587aa8)at main.cpp:52


// foo thread
pthread_once()from /lib64/libpthread.so.0
__gthread_once(__once = 0x93a084,__func = 0x4378a0< __ once_proxy @ plt>)at gthr-default.h:718
std :: call_once< void(std :: __ future_base :: _ St​​ate_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& amp;)(std :: __ future_base :: _State_base :: *&&)(std :: function< std :: unique_ptr< std :: __ future_base :: _ Result_base,...) include / c ++ / 4.7.0 / mutex:819
std :: promise< void> :: set_value(this = 0x7fff32587880)at include / c ++ / 4.7.0 / future:1206
foo :: run (this = 0x938320,p = ...)at main.cpp:26

确保我在我的代码中没有做错事,对吗?



这是pthread实现的问题,或std :: future / std :: promise实现?



我的库版本是:

  libstdc ++。 6 
libc.so.6(GNU C库稳定版本2.11.1(20100118))
libpthread.so.0(由Ulrich Drepper等人编写的本地POSIX线程库版权所有(C)2006)


解决方案

确实, local promise 对象(在构造函数的末尾和从线程调用 set_value())。也就是说, set_value()唤醒主脚步,只是下一步破坏promise对象,但 set_value()函数尚未完成和死锁。



读取C ++ 11标准,我不确定是否允许使用:


void promise< void> :: set_value();



效果:以原子方式将值r存储在共享状态,并使该状态准备就绪。



set_value,set_exception,set_value_at_thread_exit和set_exception_at_thread_exit成员函数的行为方式与它们获得单个互斥体更新promise对象时的promise对象。


set_value()应该是关于其他函数的原子,如析构函数?



IMHO,我会说不。效果将类似于销毁互斥体,而其他线程仍然锁定它。结果是未定义的。



解决方案是让 p 延长线程。我可以想到的两个解决方案:


  1. 使 p


  2. 将promise移动到该线程中。


在构造函数中:

  std :: promise< void> ; p; 
std :: future< void> f = p.get_future();
_thread = std :: thread(& foo :: run,this,std :: move(p));

BTW,你不需要调用 bind ,(线程构造函数已经重载),或者调用 std :: move 移动线程(正确的值已经是r值)。但是 std :: move 调用到promise中是必须的。



线程函数不接收一个引用,但移动的promise:

  void run(std :: promise< void> p)
{
p.set_value();
}



我认为这正是C ++ 11定义两个不同类的原因: promise future :将promise移动到线程中,但保留未来以恢复结果。 p>

I have a std::future in one thread which is waiting on a std::promise being set in another thread.

EDIT: Updated the question with an exemplar app which will block forever:

UPDATE: If I use a pthread_barrier instead, the below code does not block.

I have created a test-app which illustrates this:

Very basically class foo creates a thread which sets a promise in its run function, and waits in the constructor for that promise to be set. Once set, it increments an atomic count

I then create a bunch of these foo objects, tear them down, and then check my count.

#include <iostream>
#include <thread>
#include <atomic>
#include <future>
#include <list>
#include <unistd.h>

struct foo
{
    foo(std::atomic<int>& count)
        : _stop(false)
    {
        std::promise<void> p;
        std::future <void> f = p.get_future();

        _thread = std::move(std::thread(std::bind(&foo::run, this, std::ref(p))));

        // block caller until my thread has started 
        f.wait();

        ++count; // my thread has started, increment the count
    }
    void run(std::promise<void>& p)
    {
        p.set_value(); // thread has started, wake up the future

        while (!_stop)
            sleep(1);
    }
    std::thread _thread;
    bool _stop;
};

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        std::cerr << "usage: " << argv[0] << " num_threads" << std::endl;
        return 1;
    }
    int num_threads = atoi(argv[1]);
    std::list<foo*> threads;
    std::atomic<int> count(0); // count will be inc'd once per thread

    std::cout << "creating threads" << std::endl;
    for (int i = 0; i < num_threads; ++i)
        threads.push_back(new foo(count));

    std::cout << "stopping threads" << std::endl;
    for (auto f : threads)
        f->_stop = true;

    std::cout << "joining threads" << std::endl;
    for (auto f : threads)
    {
        if (f->_thread.joinable())
            f->_thread.join();
    }

    std::cout << "count=" << count << (num_threads == count ? " pass" : " fail!") << std::endl;
    return (num_threads == count);
}

If I run this in a loop with 1000 threads, it only has to execute it a few times until a race occurs and one of the futures is never woken up, and therefore the app gets stuck forever.

# this loop never completes
$ for i in {1..1000}; do ./a.out 1000; done

If I now SIGABRT the app, the resulting stack trace shows it's stuck on the future::wait The stack trace is below:

// main thread
    pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    __gthread_cond_wait (__mutex=<optimized out>, __cond=<optimized out>) at libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:846
    std::condition_variable::wait (this=<optimized out>, __lock=...) at ../../../../libstdc++-v3/src/condition_variable.cc:56
    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=0x93a050, __lock=..., __p=...) at include/c++/4.7.0/condition_variable:93
    std::__future_base::_State_base::wait (this=0x93a018) at include/c++/4.7.0/future:331
    std::__basic_future<void>::wait (this=0x7fff32587870) at include/c++/4.7.0/future:576
    foo::foo (this=0x938320, count=...) at main.cpp:18
    main (argc=2, argv=0x7fff32587aa8) at main.cpp:52


// foo thread
    pthread_once () from /lib64/libpthread.so.0
    __gthread_once (__once=0x93a084, __func=0x4378a0 <__once_proxy@plt>) at gthr-default.h:718
    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, ...) at include/c++/4.7.0/mutex:819
    std::promise<void>::set_value (this=0x7fff32587880) at include/c++/4.7.0/future:1206
    foo::run (this=0x938320, p=...) at main.cpp:26

I'm pretty sure that I'm not doing anything wrong in my code, right?

Is this an issue with the pthread implementation, or the std::future/std::promise implementation?

My library versions are:

libstdc++.so.6
libc.so.6 (GNU C Library stable release version 2.11.1 (20100118))
libpthread.so.0 (Native POSIX Threads Library by Ulrich Drepper et al Copyright (C) 2006)

解决方案

Indeed, there is a race condition between the destructor of the local promise object (at the end of the constructor and the call to set_value() from the thread. That is, set_value() wakes the main tread, that just next destroys the promise object, but the set_value() function has not yet finished, and dead-locks.

Reading the C++11 standard, I'm not sure if your use is allowed:

void promise<void>::set_value();

Effects: atomically stores the value r in the shared state and makes that state ready.

But somewhere else:

The set_value, set_exception, set_value_at_thread_exit, and set_exception_at_thread_exit member functions behave as though they acquire a single mutex associated with the promise object while updating the promise object.

Are set_value() calls supposed to be atomic with regards to other functions, such as the destructor?

IMHO, I'd say no. The effects would be comparable to destroying a mutex while other thread is still locking it. The result is undefined.

The solution would be to make p outlive the thread. Two solutions that I can think of:

  1. Make p a member of the class, just as Michael Burr suggested in the other answer.

  2. Move the promise into the thread.

In the constructor:

std::promise<void> p;
std::future <void> f = p.get_future();
_thread = std::thread(&foo::run, this, std::move(p));

BTW, you don't need the call to bind, (the thread constructor is already overloaded), or call to std::move to move the thread (the right value is already an r-value). The call to std::move into the promise is mandatory, though.

And the thread function does not receive a reference, but the moved promise:

void run(std::promise<void> p)
{
    p.set_value();
}

I think that this is precisely why C++11 defines two different classes: promise and future: you move the promise into the thread, but you keep the future to recover the result.

这篇关于pthread_once()中的竞争条件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆