Boost :: Future -wait_callbackは一度だけ呼び出されることが保証されていますか?
-
28-10-2019 - |
質問
設定した場合 set_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
コールバックが呼び出されている間にMutexは実際にロック解除されます。これは、私の理解から、複数のスレッドが呼び出すとコールバックが複数回呼び出される可能性があります wait
関数?
コールバックは複数回呼び出すことができますか?デザインですか?それとも私は何かが足りませんか?
私が少し驚いた理由は、C ++ 11標準で async(std::launch::deferred, ...)
(これに set_wait_callback
いとこです)、単一の呼び出し保証があるようです。
§30.6.8
共有状態は、関数が完了するまで準備ができていません。 最初の呼び出し この共有状態を参照する非同期リターンオブジェクトのタイミングのない待機機能(30.6.4)へ 待機関数と呼ばれるスレッドで延期された関数を呼び出すものとします.
解決
あなたの疑いが設立されたと思います。コードは次のように見えます
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 Semanticsをサポートすると。
これには、別のスレッドがset_wait_callbackを呼び出す可能性があるため、これにはまだいくつかの問題があります。そのため、コールバックを再割り当てし、2つのコールバックを呼び出すことができます。コールバックが既に行われている場合、追加の状態が必要であるようです。
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));
}
この問題が失われないように、スレッドをブーストするためにTRACチケットを作成できますか?