5

OpenGLアプリケーションのGUIスレッドで非同期タスクを処理するために、boost :: asio::io_serviceのラッパーを作成しました。

タスクは他のスレッドから作成される可能性があるboost::asioため、この目的には理想的であり、ミューテックスとロックを関連付けて独自のタスクキューを作成する必要がないことを意味します。各フレームで行われる作業を許容可能なしきい値(たとえば5ms)未満に保ちたいので、をpoll_one呼び出すのではなく、目的の予算を超えるまで呼び出しrunます。私が知る限り、これにはreset新しいタスクが投稿されるたびに電話をかける必要があり、これはうまく機能しているようです。

短いので、これが全部です、さん#include

typedef std::function<void(void)> VoidFunc;
typedef std::shared_ptr<class UiTaskQueue> UiTaskQueueRef;

class UiTaskQueue {

public:

    static UiTaskQueueRef create()
    {
        return UiTaskQueueRef( new UiTaskQueue() );
    }

    ~UiTaskQueue() {} 

    // normally just hand off the results of std/boost::bind to this function:
    void pushTask( VoidFunc f )
    {
        mService.post( f );
        mService.reset();
    }

    // called from UI thread; defaults to ~5ms budget (but always does one call)        
    void update( const float &budgetSeconds = 0.005f )
    {
        // getElapsedSeconds is a utility function from the GUI lib I'm using
        const float t = getElapsedSeconds();
        while ( mService.poll_one() && getElapsedSeconds() - t < budgetSeconds );
    }

private:

    UiTaskQueue() {}

    boost::asio::io_service mService;
};

メインのアプリクラスにUiTaskQueueRefのインスタンスを保持しmUiTaskQueue->update()、アプリのアニメーションループ内から呼び出します。

このクラスの機能を拡張して、タスクをキャンセルできるようにしたいと思います。以前の実装(ほぼ同じインターフェイスを使用)は、各タスクの数値IDを返し、このIDを使用してタスクをキャンセルできるようにしました。しかし、現在、キューの管理と関連するロックは、boost::asioこれを行うための最善の方法がわかりません。

キャンセルしたいタスクをでラップし、タスクにshared_ptrを格納しweak_ptr、演算子を実装して()に渡すことができるラッパーオブジェクトを作成してみましたio_service。次のようになります。

struct CancelableTask {
    CancelableTask( std::weak_ptr<VoidFunc> f ): mFunc(f) {}
    void operator()(void) const {
        std::shared_ptr<VoidFunc> f = mFunc.lock();
        if (f) {
            (*f)();
        }
    }
    std::weak_ptr<VoidFunc> mFunc;
};

次に、pushTask次のようなメソッドのオーバーロードがあります。

void pushTask( std::weak_ptr<VoidFunc> f )
{
    mService.post( CancelableTask(f) );
    mService.reset();
}

次に、以下を使用してキャンセル可能なタスクをキューに投稿します。

std::function<void(void)> *task = new std::function<void(void)>( boost::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr< std::function<void(void)> >( task );
mUiTaskQueue->pushTask( std::weak_ptr< std::function<void(void)> >( mTask ) );

または、必要にVoidFunc応じてtypedefを使用します。

VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
mUiTaskQueue->pushTask( std::weak_ptr<VoidFunc>( mTask ) );

shared_ptrmTask周りにいる限りio_service、はタスクを実行します。私が呼び出すresetmTaskweak_ptrロックできず、タスクは必要に応じてスキップされます。

私の質問は、これらすべての新しいツールに対する自信の1つです。実行しても問題はnew std::function<void(void)>( std::bind( ... ) )なく、安全に管理できるのshared_ptrでしょうか。

4

1 に答える 1

2

はい、これは安全です。

コードの場合:

VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );

ただ行う:

mTask.reset(new VoidFunc( std::bind(&MyApp::doUiTask, this) ) );

(および他の場所)。

コールバックを存続させてshared_ptrをリセットする直前に、トレッドがweak_ptrをロックする可能性がある競合状態に対処する必要があることに注意してください。その結果、コードをダウンしてもコールバックが表示されることがあります。コールバックshared_ptrをリセットするパス。

于 2011-11-07T23:11:29.923 に答える