6

나는 GCD 큐를 사용하여 자원에 대한 동시 액세스가 순차적으로 일어나는 것을 보장하기위한 몇 가지 메소드를 가진 objective-c 클래스를 가지고있다.GCD를 통해 objective-c에서 재진입 식 잠금 메커니즘을 구현하는 방법은 무엇입니까?

일부 메소드는 같은 클래스의 다른 메소드를 호출해야합니다. 따라서 잠금 장치는 재진입되어야합니다. 이것을하기위한 표준 방법이 있습니까?

은 처음에는 이러한 방법의 각 액세스를 동기화하는

dispatch_sync(my_queue, ^{ 

    // Critical section 

}); 

을 사용했다. 알다시피 이러한 메서드 중 하나가 다른 메서드를 호출하면 dispatch_sync 호출은 다른 블록이 실행될 때까지 현재 실행을 중지하기 때문에 교착 상태가 발생합니다. 큐에서 실행이 중지되기 때문에 실행도 불가능합니다. 이를 해결하기 위해 예를 들어 다음과 같이 사용했습니다. 이 방법은 :
- (void) executeOnQueueSync:(dispatch_queue_t)queue : (void (^)(void))theBlock { 
    if (dispatch_get_current_queue() == queue) { 
     theBlock(); 
    } else { 
     dispatch_sync(queue, theBlock); 
    } 
} 

그리고 내 각각의 방법에

, 나는 다른 반환 형식과 모든 블록, 나는 다른 방법을 쓸 필요가 있기 때문에 나는이 솔루션을 좋아하지 않아

[self executeOnQueueSync:my_queue : ^{ 

    // Critical section 

}]; 

를 사용합니다. 더욱이이 문제는 나에게 매우 일반적으로 보입니다.이 문제를 해결하기위한 더 나은 표준 솔루션이 있어야한다고 생각합니다.

+0

'@ synchronized'를 대신 사용 하시겠습니까? –

+1

@MartinR, 네,하지만'@ synchronized'는 GCD/대기열을 기반으로하지 않는 고전적인 잠금 장치입니다. 따라서 이해할 수 있도록 코드 단순성과 성능상의 이유로 사용하는 것이 좋습니다. 이 질문의 제목은 "잠금"을 포함하고 있기 때문에 오해의 소지가 있습니다. 내 뜻은 GDC/대기열과 재진입 방식으로 액세스를 동기화하는 것입니다. 나는 "재진입 잠금 (reentrant locking)"을 말하는 것보다 더 좋은 단어가 없었습니다. 왜냐하면 그것은 대부분의 사람들이 알고있는 문제에 대한 해결책의 이름이기 때문입니다. –

+1

@MartinR, 또한'@ synchronized'는 재진입을위한 더 간단한 코드로 이끄는 이점을 가지고 있습니다 - 무엇이든 - (이 최선을 어떻게 부르는가?). 여전히 그 뒤에있는 잠금 메커니즘은 GCD 대기열 뒤에있는 메커니즘보다 성능이 떨어집니다. –

답변

10

첫 번째 사항 먼저 : dispatch_get_current_queue()은 더 이상 사용되지 않습니다. 정식 방식은 이제 dispatch_queue_set_specific을 사용하는 것입니다. 이 패턴은 매우 사용할 수

typedef dispatch_queue_t dispatch_recursive_queue_t; 
static const void * const RecursiveKey = (const void*)&RecursiveKey; 

dispatch_recursive_queue_t dispatch_queue_create_recursive_serial(const char * name) 
{ 
    dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL); 
    dispatch_queue_set_specific(queue, RecursiveKey, (__bridge void *)(queue), NULL); 
    return queue; 
} 

void dispatch_sync_recursive(dispatch_recursive_queue_t queue, dispatch_block_t block) 
{ 
    if (dispatch_get_specific(RecursiveKey) == (__bridge void *)(queue)) 
     block(); 
    else 
     dispatch_sync(queue, block); 
} 

,하지만 당신은 dispatch_set_target_queue 중첩 된 재귀 큐를 만들고, 내부 하나의 내부에서 외부 큐에 작업을 대기열을 시도 할 수 있기 때문에, 틀림없이 방탄 아니다 : 하나의 예처럼 보일 수 있습니다 비록 당신이 이미 "자물쇠 안에"있다고하더라도 (바보 일 뿐이므로 은 자물쇠처럼으로 보입니다. 실제로는 다른 것입니다 : 대기열 - 따라서 질문은, 맞습니까?) 바깥 쪽을 위해서. (당신은 등, dispatch_set_target_queue에 전화를 포장하고 그래프를 대상으로 대역 외 자신을 유지하여 그 주위를 얻을 수 있지만, 독자를위한 운동으로 남아.)

을 당신은 말을 계속 :

반환 유형이 다른 인 모든 블록에 대해 다른 방법을 사용해야하므로이 솔루션이 마음에 들지 않습니다.

이 "상태 보호 직렬 대기열"패턴의 일반적인 개념은 개인 상태를 보호한다는 것입니다. 왜 당신은 "자신의 대기열을 가져다 줄까?" 그것이 상태 보호를 공유하는 여러 객체에 관한 것이라면, 대기열을 찾는 고유 한 방법을 제공하십시오 (즉, 초기화시이를 밀어 넣거나 모든 이해 당사자가 상호 액세스 할 수있는 곳에 배치하십시오). 여기에 "대기열을 가져 오는 것"이 ​​유용 할지는 분명하지 않습니다.

+2

deprecated 함수에 대한 설명과 대신 사용하는 방법 및 방법에 대해 감사드립니다. 그것만으로도 가치가 있습니다. –

+0

@ipmcc,이 주제와 링크 된 주제에 대한 귀하의 답변과 의견에 깊은 인상을 받았습니다. 당신은 또한이 [질문]보십시오 (http://stackoverflow.com/questions/20201078/how-to-implement-a-reentrant-locking-mechanism-through-dispatch-concurrent-queue) 방금 게시했습니다. ? –

+0

@ipmcc 레이블이 &'dispatch_queue_get_label (DISPATCH_CURRENT_QUEUE_LABEL)'인 큐를 사용하는 것과는 대조적으로이 접근법의 효율성이나 효율성에는 차이가 있습니까? – Orangenhain