직렬 대기열에서 dispatch_async와 dispatch_sync의 차이점은 무엇입니까?


125

다음과 같이 직렬 대기열을 만들었습니다.

    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);

dispatch_async이처럼 호출 의 차이점은 무엇입니까

 dispatch_async(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_async(_serialQueue, ^{ /* TASK 2 */ });

그리고 dispatch_sync이 일련 큐에 다음과 같이이라고?

 dispatch_sync(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_sync(_serialQueue, ^{ /* TASK 2 */ });

내 이해는 어떤 디스패치 방법을 사용하든 TASK 1전에 실행되고 완료된다는 것입니다 TASK 2.

답변:


409

예. 직렬 대기열을 사용하면 작업의 직렬 실행이 보장됩니다. 유일한 차이점은 dispatch_sync블록이 완료된 후에 만 ​​리턴하는 반면 dispatch_async큐에 추가 된 후에 리턴하면 완료되지 않을 수 있다는 것입니다.

이 코드

dispatch_async(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_async(_serialQueue, ^{ printf("3"); });
printf("4");

그것은 인쇄 할 수 있습니다 2413또는 2143또는 1234하지만 1항상 전3

이 코드

dispatch_sync(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_sync(_serialQueue, ^{ printf("3"); });
printf("4");

항상 인쇄 1234


참고 : 첫 번째 코드의 경우 인쇄 되지 않습니다1324 . 가 실행 된 후에printf("3") 전달 되기 때문 입니다 . 작업 은 발송 된 후에 만 실행할 수 있습니다 . printf("2")


작업 실행 시간은 변경되지 않습니다. 이 코드는 항상 인쇄12

dispatch_async(_serialQueue, ^{ sleep(1000);printf("1"); });
dispatch_async(_serialQueue, ^{ printf("2"); });

일어날 수있는 일은

  • 스레드 1 : 시간이 많이 걸리는 작업 (작업 1)을 serial queue로 dispatch_async
  • 스레드 2 : 태스크 1 시작
  • 스레드 1 : 다른 작업 (작업 2)을 직렬 대기열로 dispatch_async
  • 스레드 2 : 작업 1 완료 작업 2 시작
  • 스레드 2 : 작업 2 완료

그리고 당신은 항상 볼 12


7
또한 2134와 1243을 인쇄 할 수 있습니다
Matteo Gobbi

내 질문은 왜 우리가 정상적인 방법으로하지 않았습니까? printf("1");printf("2") ;printf("3") ;printf("4")dispatch_sync
androniennn

두 번째 예는 @androniennn? 다른 스레드가 dispatch_sync(_serialQueue, ^{ /*change shared data*/ });동시에 실행될 수 있기 때문 입니다.
Bryan Chen

1
@ asma22 여러 스레드 / 디스패치 큐간에 스레드에 안전한 객체를 공유하는 것이 매우 유용합니다. 직렬 대기열의 개체에만 액세스하면 안전하게 액세스하고있는 것입니다.
Bryan Chen

1
나는 연속 실행을 의미한다 . 모든 작업이 동일한 대기열의 다른 작업과 관련하여 연속적으로 실행된다는 관점에서 볼 때. 원인은 여전히 ​​다른 큐와 동시에 고려 될 수 있습니다. 작업을 동시에 발송하고 실행할 수있는 것은 GCD의 요점입니다.
Bryan Chen

19

차이 dispatch_sync와는 dispatch_async간단하다.

두 예제 모두에서 TASK 1항상 TASK 2전달되기 때문에 항상 전에 실행 됩니다.

dispatch_sync그러나이 예 에서는 TASK 2after TASK 1을 디스패치 하고 실행할 때까지 디스패치하지 않습니다 . 이것을 "차단"이라고 합니다. 코드는 작업이 실행될 때까지 대기합니다 (또는 "차단").

dispatch_async예제에서 코드는 실행이 완료되기를 기다리지 않습니다. 두 블록 모두 대기열로 전달되고 대기열에 추가되며 나머지 코드는 해당 스레드에서 계속 실행됩니다. 그런 다음 (언제든지 큐에 디스패치 된 내용에 따라) 어느 시점에서 Task 1실행 된 다음 Task 2실행됩니다.


2
주문이 잘못되었다고 생각합니다. 첫 번째 예는 async비 차단 버전입니다.
Bryan Chen

내가 생각 하는 바에 대한 답변을 수정했습니다 . 그렇지 않은 경우 변경하고 명확히하십시오.
JRG- 개발자

1
dispatch_sync를 호출 한 다음 같은 큐에서 dispatch_async를 호출하면 어떻게됩니까? (및 그 반대)
0xSina

1
직렬 대기열에서 두 작업은 계속해서 실행됩니다. 첫 번째 경우, 호출자는 첫 번째 블록이 끝날 때까지 기다리지 만 두 번째 블록을 기다리지 않습니다. 두 번째 경우, 호출자는 첫 번째 블록이 끝날 때까지 기다리지 않고 두 번째 블록을 기다립니다. 그러나 큐는 순서대로 블록을 실행하기 때문에 호출자는 효과적으로 두 블록이 완료되기를 기다립니다.
gnasher729

1
블록은 자체 큐에서 dispatch_async를 수행 할 수도 있습니다 (나중에 실행될 블록 추가). 자체 직렬 대기열 또는 기본 대기열의 dispatch_sync가 교착 상태가됩니다. 이 상황에서 호출자는 원래 블록이 완료 될 때까지 기다리지 만 다른 블록은 기다리지 않습니다. dispatch_sync는 블록을 큐의 끝에 놓고 블록은 블록이 완료 될 때까지 코드를 실행 한 다음 dispatch_sync가 리턴합니다. dispatch_async는 대기열 끝에 블록을 추가합니다.
gnasher729

5

모두 메인 큐와 관련이 있습니다. 순열은 4 개입니다.

i) 시리얼 대기열, 디스패치 비동기 : 여기서 작업이 차례로 실행되지만 주 스레드 (UI에 미치는 영향)는 반환을 기다리지 않습니다.

ii) 시리얼 큐, 디스패치 동기화 : 여기서 작업이 차례대로 실행되지만 메인 스레드 (UI에 미치는 영향)가 지연됩니다

iii) 동시 대기열, 디스패치 비동기 : 여기서 작업이 병렬로 실행되고 주 스레드 (UI에 영향)가 반환을 기다리지 않고 부드럽게됩니다.

iv) 동시 대기열, 디스패치 동기화 : 여기서 작업이 병렬로 실행되지만 주 스레드 (UI에 미치는 영향)가 지연됩니다

동시 또는 직렬 대기열의 선택은 다음 작업에 대한 이전 작업의 출력이 필요한지 여부에 따라 다릅니다. 이전 작업에 의존하는 경우 직렬 대기열을 채택하고 동시 대기열을 사용하십시오.

그리고 마지막으로 이것은 우리가 사업을 마쳤을 때 주 스레드로 다시 침투하는 방법입니다.

DispatchQueue.main.async {
     // Do something here
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.