Boost :: Future - wait_callback гарантированно будет вызван только один раз?

StackOverflow https://stackoverflow.com/questions/9355110

  •  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 для повышения потока, чтобы эта проблема не была потеряна?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top