2011-11-07 63 views
4

我創建了一個boost :: asio :: io_service的包裝來處理OpenGL應用程序的GUI線程上的異步任務。正在使用shared_ptr和weak_ptr來管理std :: function安全的生命週期?

任務可能是由其他線程創建的,所以boost::asio對於這個目的來說似乎是理想的,並且意味着我不需要用關聯的互斥鎖和鎖定編寫我自己的任務隊列。我希望將每幀的工作都保持在可接受的閾值以下(例如5毫秒),所以我打電話給poll_one,直到超出所需的預算,而不是致電run。據我所知,這需要我在發佈新任務時致電reset,這似乎運作良好。

由於它是短,這裏的整個事情,SANS #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將執行任務。如果我在mTask上調用reset,則weak_ptr無法鎖定,並且按需要跳過任務。

我的問題真的是所有這些新工具的一個信心:是new std::function<void(void)>(std::bind(...))一個好的事情要做,並安全的事情來管理一個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

相關問題