boost::future-是否保证wait_callback只被调用一次

boost::future - Is wait_callback guaranteed to only be invoked once?

本文关键字:调用 一次 callback 是否 future- wait boost      更新时间:2023-10-16

如果在boost::unique_future上设置set_wait_callback,它是否保证只运行一次?

我有点怀疑,因为当查看源代码时,我发现以下内容:

struct relocker
{
    boost::unique_lock<boost::mutex>& lock;
    relocker(boost::unique_lock<boost::mutex>& lock_):
        lock(lock_)
    {
        lock.unlock();
    }
    ~relocker()
    {
        lock.lock();
    }
private:
    relocker& operator=(relocker const&);
};
void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && !done)
    {
        boost::function<void()> local_callback=callback;
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    
void wait(bool rethrow=true)
{
    boost::unique_lock<boost::mutex> lock(mutex);
    do_callback(lock);
    while(!done)
    {
        waiters.wait(lock);
    }
    if(rethrow && exception)
    {
        boost::rethrow_exception(exception);
    }
}

do_callback中,当回调被调用时,互斥对象实际上是解锁的,根据我的理解,如果多个线程调用wait函数,这可能会导致回调被多次调用?

可以多次调用回调吗?它是故意的吗?还是我错过了什么?

我有点惊讶的原因是,在C++11标准中,async(std::launch::deferred, ...)set_wait_callback是其表亲)似乎有一个单独的调用保证:

§30.6.8

在功能完成之前,共享状态不会准备就绪。对引用该共享状态的异步返回对象应调用调用等待函数的线程中的延迟函数

我认为你的怀疑是有根据的。代码应该看起来像

void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && !done)
    {
        boost::function<void()> local_callback=callback;
        callback=boost::function<void()>;
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    

甚至

void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && !done)
    {
        boost::function<void()> local_callback=boos::move(callback);
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    

一旦Boost.Function将支持move语义。

这还有一些问题,因为另一个线程可能会调用set_wait_callback,所以可以重新分配回调,并且可以调用两个回调。似乎需要一个额外的状态来说明回调是否已经完成。

void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && ! callback_done && !done)
    {
        boost::function<void()> local_callback=callback;
        callback_done=true;
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    

顺便说一句,set_wait_callback不是线程安全的。

    template<typename F,typename U>
    void set_wait_callback(F f,U* u)
    {
        callback=boost::bind(f,boost::ref(*u));
    }

并且必须受到保护

    template<typename F,typename U>
    void set_wait_callback(F f,U* u)
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        callback=boost::bind(f,boost::ref(*u));
    }

请,你能为Boost Thread创建一个Trac票证,这样这个问题就不会丢失吗?