2014-09-09 1 views
4

이 상황이 있습니다. 비디오는 프레임 단위로 처리되어야하지만 프레임이 처리되는 동안 출력은 파일에 순차적으로 기록되어야합니다.비동기식으로 실행하지만 동 기적으로 쓰기

프로세스를 가속화하기 위해 동시 대기열에 dispatch_async을 사용하여 비동기 블록을 실행하려고하지만이 대기열이 비동기이기 때문에 출력에 프레임을 연속적으로 쓰는 방법을 알지 못합니다.

이 상황에서는 프레임 1, 2, 3, 4 및 5가 처리를 위해 동시 대기열로 보내집니다. 어떤 블록이라도 언제든지 끝낼 수 있기 때문에 프레임 4가 처음으로 끝나고 5, 3, 1, 2가 뒤따라야합니다. 그럼 순차적 순서로 프레임을 출력에 쓸 수 있습니까?

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

while (true) { 

    video >> frame; // read a frame from the video 

    dispatch_async(aQueue, ^{ 
     processVideo(frame, outputFrame); 
     writeToVideo(outputFrame); // this is here just to show what needs to be done 
    }); 

    // bla bla 

} 

단서 :

나는 이런 코드가?

감사

답변

3

나는 직렬 디스패치 대기열과 NSCondition의 조합을 사용합니다. 직렬 대기열은 어떤 쓰기도 동시에 발생하지 않도록하며, NSCondition은 올바른 순서로 발생하는지 확인합니다. NSCondition 문서에서

: 잠금 주어진 스레드에서 체크 포인트를 모두 같은

조건 개체 역할을합니다. 잠금은 조건을 테스트하는 동안 코드를 보호하고 은 조건에 의해 트리거 된 작업을 수행합니다. 검사 점 동작 은 스레드가 작업으로 진행하기 전에 조건이 참이어야합니다. 조건이 true가 아닌 동안 스레드가 차단됩니다. 나는 이런 식으로 뭔가를 할 거라고 특정 상황에서

... 루프에서

먼저 정의하라 BOOL (처음 NO로 설정)하여 프레임이 처리되었는지의 여부를 나타내는있는, 그리고 NSCondition. 그런 다음 프레임을 처리 할 백그라운드 큐와 데이터를 쓰는 직렬 큐 모두에 dispatch_async을 입력합니다.

직렬 대기열의 블록이 실행되면 NSCondition을 잠근 다음 BOOL을 검사하여 프레임이 처리되었는지 확인하십시오. 있는 경우 쓰기를 진행하십시오. 그렇지 않은 경우 signal의 경우 waitNSCondition에서 수신 한 다음 다시 확인하십시오. 완료되면 unlockNSCondition입니다.

백그라운드 큐의 블록이 실행되면 NSCondition을 잠근 후 프레임을 처리하십시오. 프레임이 처리되면 BOOL을 설정하여 프레임이 처리되었음을 나타냅니다. 그런 다음 signalunlockNSCondition입니다.

참고 : 그것은 당신 만 NSCondition의 잠금 내부 프레임이 처리되고 있음을 나타냅니다 BOOL하고 outputFrame에 액세스하는 것이 중요합니다; 자물쇠가 스레드간에 동기화 상태를 유지하는지 확인합니다.

// Create the background and serial queues 
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_queue_t writeQueue = dispatch_queue_create("writeQueue", DISPATCH_QUEUE_SERIAL); 

while (true) { // I'm assuming you have some way to break out of this... 
    NSCondition *condition = [[NSCondition alloc] init]; 

    // These need the __block attribute so they can be changed inside the blocks 
    __block BOOL frameProcessed = NO; 
    __block FrameType outputFrame = nil; 

    // video >> frame; // read a frame from the video 

    // dispatch the frame for processing 
    dispatch_async(backgroundQueue, ^{ 
     [condition lock]; 

     processVideo(frame, outputFrame); 
     frameProcessed = YES; 

     [condition signal]; 
     [condition unlock]; 
    }); 

    // dispatch the write 
    dispatch_async(writeQueue, ^{ 
     [condition lock]; 
     while (!frameProcessed) { 
      [condition wait]; // this will block the current thread until it gets a signal 
     } 

     writeToVideo(outputFrame); 

     [condition unlock]; 
    }); 
} 

참고 :도 위의 코드에서 BOOL frameProcessed와 반 미묘한 트릭이있다. 외부 대신 루프 내부에 선언되었으므로 각 블록은 프레임과 연결된 블록을 캡처합니다.


업데이트 :뿐만 아니라 독서에 대한 NSCondition 추가.

비디오에 쓰는 것은 병렬 실행에 비해 느리기 때문에

는 프레임의 엄청나게 할당하고이 디스크에 저장 때까지 메모리에 앉아있다. 나는이 문제를 해결할 것

는 당신이 기다리고 너무 많은 프레임이있는 경우 읽기 블록 다른 NSConditionwriteQueue에 쓸 수 사용 읽어 조절하는 것입니다. 이 개념은 이전에 추가 한 NSCondition과 거의 동일합니다. 단지 다른 조건 일뿐입니다. 이 캐스트에서 쓸 수있는 프레임 수를 나타내는 int이됩니다.

루프를 수행하기 전에 readCondition, writeQueueSizemaxWriteQueueSize을 정의하십시오. 루프 내부에서 먼저 lockreadCondition을 확인하고 writeQueueSize >= maxWriteQueueSize을 확인합니다. 그렇지 않은 경우 프레임을 읽고 처리 및 기록을 대기열에 올려 놓으십시오. writeQueue으로 파견하기 바로 전에 writeQueueSize을 증분하십시오. 그 다음 unlockreadCondition.

그리고, 내부 블록은 쓰기가 완료되면, writeQueuelockreadCondition을 파견 writeQueueSizesignalunlockreadCondition을 감소시킵니다.

이렇게하면 writeQueue에서 대기중인 블록이 maxWriteQueueSize 개 이상이어야합니다. 대기중인 블록이 많으면 writeQueue이 준비 될 때까지 비디오에서 프레임 읽기를 효과적으로 일시 중지합니다.

// Create the background and serial queues 
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_queue_t writeQueue = dispatch_queue_create("writeQueue", DISPATCH_QUEUE_SERIAL); 

NSCondition *readCondition = [[NSCondition alloc] init]; 
__block int writeQueueSize = 0; 
const int maxWriteQueueSize = 10; 

while (true) { // I'm assuming you have some way to break out of this... 
    NSCondition *writeCondition = [[NSCondition alloc] init]; 

    // These need the __block attribute so they can be changed inside the blocks 
    __block BOOL frameProcessed = NO; 
    __block FrameType outputFrame = nil; 

    [readCondition lock]; 
    while (writeQueueSize >= maxWriteQueueSize) { 
     [readCondition wait]; 
    } 

    // video >> frame; // read a frame from the video 

    // dispatch the frame for processing 
    dispatch_async(backgroundQueue, ^{ 
     [writeCondition lock]; 

     processVideo(frame, outputFrame); 
     frameProcessed = YES; 

     [writeCondition signal]; 
     [writeCondition unlock]; 
    }); 

    // dispatch the write 
    writeQueueSize++; // Increment the write queue size here, before the actual dispatch 
    dispatch_async(writeQueue, ^{ 
     [writeCondition lock]; 
     while (!frameProcessed) { 
      [writeCondition wait]; // this will block the current thread until it gets a signal 
     } 

     writeToVideo(outputFrame); 

     [writeCondition unlock]; 

     // Decrement the write queue size and signal the readCondition that it changed 
     [readCondition lock]; 
     writeQueueSize--; 
     [readCondition signal]; 
     [readCondition unlock]; 
    }); 

    [readCondition unlock]; 
} 
+0

환상적인! 이것은 단순히 훌륭합니다 !!!!!!!!!!!!!!!!!!! 충분히 upvote 수 없습니다 !!!! – SpaceDog

+0

이 방법에는 단 하나의 문제 만 있습니다. 비디오에 대한 쓰기가 병렬 실행에 비해 느리기 때문에 많은 양의 프레임이 할당되고 디스크에 저장 될 때까지 메모리에 저장됩니다. 메모리 사용이 10 초 만에 42Mb에서 2GB로 확대되었습니다! – SpaceDog

+0

내가 도와 줘서 다행이다 :) 나는 당신이 당신의 쓰기 큐를 압도하지 않도록 읽기 속도를 조절하는 방법으로 나의 대답을 업데이트했다. –

0

는 각 프레임에게 자신의 결과 큐를 제공하고 위해 함께 모든 큐 체인이 작업을 수행 할 수 있습니다. 첫 번째 대기열을 제외한 모든 대기열을 일시 중단합니다. 그런 다음 각 프레임이 완료되면 다음 결과 대기열을 다시 시작합니다. 이렇게하면 대기열이 작업 완료 시점에 관계없이 원하는 순서대로 결과를 전달하게됩니다.

임의의 양의 작업을 시뮬레이트하기 위해 sleep을 사용하고 결과를 올바른 순서로 인쇄하는 예제입니다. dispatch_group은 프로그램이 너무 빨리 종료되지 않도록하기 위해 여기에 사용됩니다. 귀하의 경우에는 필요하지 않을 수도 있습니다.

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
    dispatch_queue_t mainQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT); 
    dispatch_group_t group = dispatch_group_create(); 

    dispatch_queue_t myQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 

    for (unsigned x = 1; x <= 5; x++) { 

     // Chain the queues together in order; suspend all but the first. 
     dispatch_queue_t subQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 
     dispatch_set_target_queue(subQueue, myQueue); 
     dispatch_suspend(subQueue); 

     dispatch_group_async(group, mainQueue,^{ 

     // Perform a random amount of work 
     u_int32_t sleepTime = arc4random_uniform(10); 
     NSLog(@"Sleeping for thread %d (%d)", x, sleepTime); 
     sleep(sleepTime); 

     // OK, done with our work, queue our printing, and tell the next guy he can print 
     dispatch_sync(myQueue, ^{ 
      printf("%d ", x); 
      dispatch_resume(subQueue); 
     }); 
     }); 

     myQueue = subQueue; 
    } 

    // Wait for the whole group to finish before terminating 
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
    } 

    return 0; 
}