如果我在set_wait_callback
上设置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) 引用此共享状态的异步返回对象将调用 调用等待函数的线程中的延迟函数。
答案 0 :(得分: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();
}
}
BTW,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票,这样这个问题不会丢失吗?