2011-11-07 8 views
4

OpenGLアプリケーションのGUIスレッドで非同期タスクを処理するためにboost :: asio :: io_serviceのラッパーを作成しました。shared_ptrとweak_ptrを使用してstd :: function safeのライフタイムを管理していますか?

他のスレッドからタスクが作成される可能性があります。boost::asioは、この目的には理想的と思われ、関連するmutexとロックを持つ独自のタスクキューを作成する必要はありません。私はrunを呼び出すのではなく、希望の予算を超過するまでpoll_oneを呼び出していますので、各フレームで行われた作業を許容可能なしきい値(たとえば5ms)以下に保ちたいと思います。私が知る限り、新しい仕事が投稿されるたびに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に渡すことができるよう()オペレータを実装ラッパーオブジェクトを作成することによって試みを行いました。それは次のようになります。私は、キューにポスト解約タスクが使用して

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

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メソッドのオーバーロードを持っ

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はそのタスクを実行します。 resetmTaskと呼び出すと、weak_ptrはロックできず、タスクは必要に応じてスキップされます。

私の質問は、これらすべての新しいツールでは本当に自信があります:new std::function<void(void)>(std::bind(...)) OKのことです。shared_ptrで管理するのは安全でしょうか?

答えて

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をリセットしているコードパスをダウンしました。

+0

ありがとうございました!私は、タスクが処理されるUIスレッドから取り消すだけで競合状態を回避できると思いますが、このコードのスレッドバージョンでは注意しています。 – RandomEtc

+0

あまりにも初期化のヒントありがとう! – RandomEtc

関連する問題