Boost :: Future - wait_callback гарантированно будет вызван только один раз?
-
28-10-2019 - |
Вопрос
Если я настрою 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) на асинхронном объекте возврата, относящейся к этому общему состоянию должен вызвать отложенную функцию в потоке, который называется функцией ожидания.
Решение
Я думаю, что ваше подозрение основано. Код должен выглядеть как
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));
}
Пожалуйста, не могли бы вы создать билет Trac для повышения потока, чтобы эта проблема не была потеряна?