2

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

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

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

在函数完成之前,共享状态不会准备好。 在引用此共享状态的异步返回对象上首次调用非定时等待函数 (30.6.4)应调用调用等待函数的线程中的延迟函数

4

1 回答 1

2

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

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 将支持移动语义。

这还有一些问题,因为另一个线程可以调用 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 票证,这样这个问题就不会丢失?

于 2012-12-15T11:45:11.593 回答