2

に を設定する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) への最初の呼び出しは、待機関数を呼び出したスレッドで遅延関数を呼び出すものとします

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 を呼び出す可能性があるため、コールバックが再割り当てされ、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));
    }

この問題が失われないように、Boost Thread への Trac チケットを作成していただけませんか?

于 2012-12-15T11:45:11.593 に答える