2011-11-07 6 views
4

OpenGL 응용 프로그램의 GUI 스레드에서 비동기 작업을 처리하기 위해 boost :: asio :: io_service에 대한 래퍼를 만들었습니다.shared_ptr 및 weak_ptr을 사용하여 std :: function safe의 수명을 관리합니까?

다른 스레드에서 작업을 생성 할 수 있으므로 boost::asio이 이상적으로 보이고 관련 뮤텍스 및 잠금 기능이있는 자체 작업 큐를 작성할 필요가 없습니다. 허용되는 임계 값 (예 : 5ms) 아래에서 각 프레임의 작업을 계속 유지하여 run을 호출하는 것보다 원하는 예산이 초과 될 때까지 poll_one으로 전화를 걸겠습니다. 제가 말할 수있는 한, 새로운 작업이 게시 될 때마다 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; 
}; 

I는 다음과 같습니다 내 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