λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

🍎 iOS/iOS Programming

[iOS] Concurrency(λ™μ‹œμ„±) ν”„λ‘œκ·Έλž˜λ°(1) - Concurrency(λ™μ‹œμ„±) ν”„λ‘œκ·Έλž˜λ°μ΄λž€?

728x90

 

iOSμ—μ„œ Concurrency(λ™μ‹œμ„±) ν”„λ‘œκ·Έλž˜λ°μ— λŒ€ν•΄ μ•Œμ•„λ³΄μž.

 


  • Concurrency(λ™μ‹œμ„±) ν”„λ‘œκ·Έλž˜λ°μ΄λž€ ?
  • GCD/Operation?
  • DispatchQueue 의 μ’…λ₯˜
    • Synchronous(동기) VS Asynchronous(비동기)
    • Serial(직렬) VS Concurrency(λ™μ‹œ)

πŸ”₯ Concurrency(λ™μ‹œμ„±) ν”„λ‘œκ·Έλž˜λ°μ΄λž€ ?

같은 μ‹œκ°„ 내에 λ‹€μˆ˜μ˜ μž‘μ—…μ΄ μ‹€ν–‰λ˜λŠ” ν”„λ‘œκ·Έλž˜λ°

ν•œ μ“°λ ˆλ“œ μ•ˆμ— λ§Žμ€ μ–‘μ˜ Taskκ°€ μŒ“μ΄λ©΄μ„œ 속도 지연, μ„±λŠ₯ μ €ν•˜ λ“± λ°œμƒν•˜λŠ” 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•œ λ°©λ²•μœΌλ‘œ

λ‹€λ₯Έ μ“°λ ˆλ“œλ‘œ λΆ„μ‚°μ‹œμΌœμ„œ 문제λ₯Ό ν•΄κ²°ν•œλ‹€.

보톡 μŠ€λ ˆλ“œ 처리λ₯Ό ν•˜λŠ” μž‘μ—…λ“€μ€ μ‹œκ°„μ΄ κ½€λ‚˜ κ±Έλ¦¬λŠ” 큰 μž‘μ—…μ΄κ±°λ‚˜ μ–Έμ œ 끝날지 λͺ¨λ₯΄λŠ” μ•Œ 수 μ—†λŠ” μž‘μ—…μ— μ‚¬μš©λœλ‹€. 예λ₯Ό λ“€μ–΄ λ„€νŠΈμ›Œν¬, νŒŒμΌλ‘œλ”© λ“±.

 

ν•œ μ“°λ ˆλ“œ μ•ˆμ— μŒ“μΈ λ§Žμ€ Taskλ“€
λ‹€λ₯Έ μ“°λ ˆλ“œλ“€λ‘œ λΆ„μ‚°μ‹œν‚΄

κ·Έλ ‡λ‹€λ©΄, μž‘μ—…μ„ μ–΄λ–»κ²Œ λΆ„μ‚° μ‹œν‚¬ 수 μžˆμ„κΉŒ?

μž‘μ—…μ„ μ–΄λ–»κ²Œ λ‹€λ₯Έ μ“°λ ˆλ“œμ—μ„œ λ™μ‹œμ— 일을 ν•˜κ²Œ ν•  수 μžˆμ„κΉŒ?

 

iOS의 λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°μ€ λŒ€κΈ°ν–‰λ ¬(Queue)에 λ„£κΈ°λ§Œ ν•˜λ©΄ μ•Œμ•„μ„œ OSκ°€ μ•Œμ•„μ„œ μ“°λ ˆλ“œλ₯Ό μ°Ύμ•„μ„œ λΆ„μ‚°μ²˜λ¦¬(μ“°λ ˆλ“œ 관리)ν•œλ‹€.

μ΄λ•Œ, μ„ μž…μ„ μΆœλ‘œ(FIFO)둜 λ™μž‘

 

λ”°λΌμ„œ, κ°œλ°œμžκ°€ ν•΄μ•Όν•  일은 Queue둜 Task듀을 λ³΄λ‚΄λŠ” 것이닀.


πŸ”₯ GCD/Operation

μœ„μ—μ„œ λ§ν•œ Queue둜 Task듀을 λ³΄λ‚΄λŠ” 방법이닀.

iOS κ°œλ°œμ—μ„œ 주둜 μ‚¬μš©λ˜κ³  μžˆλŠ” λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ° API 2κ°€μ§€λ‘œ GCD, Operationκ°€ μžˆλ‹€.

  • μ§μ ‘μ μœΌλ‘œ μ“°λ ˆλ“œλ₯Ό κ΄€λ¦¬ν•˜μ§€ μ•Šκ³ , "큐(Queue, λŒ€κΈ°ν–‰λ ¬)"λΌλŠ” κ°œλ…μ„ μ΄μš©ν•΄ μž‘μ—…μ„ λΆ„μ‚°μ²˜λ¦¬

μ—¬κΈ°μ„œ κ°„λ‹¨ν•˜κ²Œ 보고 μΆ”ν›„ μžμ„Έν•˜κ²Œ λ‹€λ£° μ˜ˆμ •!

 

βœ”οΈ GCD(Grand Central Dispatch)

  • DispatchQueue μ‚¬μš©
  • Low level API 둜 μŠ€λ ˆλ“œ 풀을 κ΄€λ¦¬ν•˜λ©° dispatch queue에 μžˆλŠ” μž‘μ ‘λ“€μ„ μ‚¬μš©ν•œ κ°€λŠ₯ν•œ μŠ€λ ˆλ“œμ— μŠ€μΌ€μ₯΄λ§
  • κ°œλ°œμžλŠ” μŠ€λ ˆλ“œ 관리에 신경쓰지 μ•Šμ•„λ„ 되며 Task듀을 dispatch queue에 λ„£μ–΄μ£Όλ©΄ 됨.
  • κ°„λ‹¨ν•œ 일, ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” μž‘μ—…(λ©”μ„œλ“œ μœ„μ£Ό)μ—μ„œ μ‚¬μš©

[예제 μ½”λ“œ]

// μ•„λž˜ μ½”λ“œμ˜ 의미
//"큐둜 보낼꺼야","νμ˜μ’…λ₯˜","λΉ„λ™κΈ°μ μœΌλ‘œ"
DispatchQueue.global().async {
    
    //λ‹€λ₯Έ μ“°λ ˆλ“œλ‘œ 보낼 μž‘μ—…μ„ 배치
    //이 ν΄λ‘œμ € ν•˜λ‚˜κ°€ μž‘μ—…μ˜ 뭉탱이
}

 

βœ”οΈ Operation

  • Operation Queue μ‚¬μš©
  • GCDλ₯Ό 기반으둜 생성됨
    • Operation : GCD + μ—¬λŸ¬κ°€μ§€ κΈ°λŠ₯(μž‘μ—… μ·¨μ†Œ, μˆœμ„œμ§€μ •, μ˜μ‘΄μ„± λ“±)
  • λ³΅μž‘ν•œ 일 
  • μž¬μ‚¬μš©μ„± : 데이터와 κΈ°λŠ₯을 μΊ‘μŠν™”ν•œ 객체
    • μž‘μ—… λ‹¨μœ„ ν˜Ήμ€ μž‘μ—…μ„ λž˜ν•‘ν•˜κ³  λ‚˜μ€‘μ— μ‹€ν–‰ν•  수 있으며 ν•΄λ‹Ή μž‘μ—… λ‹¨μœ„λ₯Ό μ—¬λŸ¬λ²ˆ 제좜 κ°€λŠ₯

πŸ”₯ Synchronous(동기) VS Asynchronous(비동기) 

βœ”οΈ Synchronous(동기)

  • 첫번째 μž‘μ—… μ‹œμž‘ ν›„ μž‘μ—…μ΄ λλ‚˜λŠ” 것을 κΈ°λ‹€λ¦° ν›„ λ‹€μŒ μž‘μ—…μ„ μ²˜λ¦¬ν•œλ‹€.
  • 즉, μž‘μ—…μ΄ λλ‚˜κΈ° μ „κΉŒμ§€ λ©”μΈμ“°λ ˆλ“œ(Thread1)κ°€ λ‹€λ₯Έ μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ§€ λͺ»ν•œλ‹€.
  • *λ™κΈ°μ μœΌλ‘œ λ³΄λ‚΄λŠ” μ½”λ“œλ₯Ό μ§œλ”λΌλ„ μ‹€μ§ˆμ μœΌλ‘œ λ©”μΈμ“°λ ˆλ“œμ—μ„œ 순차적으둜 처리 됨.(λ™κΈ°μ½”λ“œμ™€ λ™μΌν•˜κ²Œ μž‘μ—…)

[동기 μ½”λ“œ]

DispatchQueue.main.sync {
            // Main νμ—μ„œ 동기 λ°©μ‹μœΌλ‘œ μ‹€ν–‰ν•  μ½”λ“œ
}

>> 많이 μ‚¬μš© μ•ˆν•œλ‹€.

WHY ? 처리 μ‹œκ°„을 쀄이기 μœ„ν•˜μ—¬ λΉ„동기λ₯Ό μ‚¬μš©(Queueλ₯Ό 톡해 μ“°λ ˆλ“œ λΆ„μ‚°)ν•˜λŠ”κ±°λΌ λ™κΈ°λŠ” 거의 μ‚¬μš© μ•ˆν•¨.

 

 

βœ”οΈ  Asynchronous(비동기)

  • 첫번째 μž‘μ—… μ‹œμž‘ ν›„ μž‘μ—…μ΄ λλ‚˜λŠ” 것을 κΈ°λ‹€λ¦¬μ§€ μ•Šκ³  λ°”λ‘œ λ‹€μŒ μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” 것
  • λ©”μΈμ“°λ ˆλ“œ(Thread1)κ°€ λ‹€λ₯Έ 일 처리λ₯Ό μ‹œμž‘ν•  수 μžˆλ‹€.
  • μ—¬λŸ¬κ°€μ§€ μž‘μ—…μ„ 'λ™μ‹œμ—' 처리 κ°€λŠ₯!!

*"비동기" κ°œλ…μ΄ ν•„μš”ν•œ μ΄μœ λŠ” 

λŒ€λΆ€λΆ„ μ„œλ²„μ™€μ˜ 톡신(λ„€νŠΈμ›Œν¬ μž‘μ—…) λ•Œλ¬Έ

λ„€νŠΈμ›Œν¬μ™€ κ΄€λ ¨λœ μž‘μ—…λ“€μ€ λ‚΄λΆ€μ μœΌλ‘œ λΉ„λ™κΈ°μ μœΌλ‘œ κ΅¬ν˜„λ˜μ–΄ 있음

[비동기 μ½”λ“œ]

DispatchQueue.main.async {
            // Main νμ—μ„œ 비동기 λ°©μ‹μœΌλ‘œ μ‹€ν–‰ν•  μ½”λ“œ
            // 즉, μ‹€ν–‰ν•˜κ³  기닀리지 μ•ŠλŠ” μ½”λ“œ 
}

 

[동기, 비동기 μž‘μ—… 예제 μ½”λ“œ 비ꡐ]

μ•„λž˜ μ½”λ“œμ—μ„œ task듀을 동기, 비동기 μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ—¬ 좜λ ₯ 값을 비ꡐ해 보자.

func task1() {
    print("Task 1 μ‹œμž‘")
    sleep(1)
    print("Task 1 μ™„λ£Œβ˜…")
}

func task2() {
    print("Task 2 μ‹œμž‘")
    print("Task 2 μ™„λ£Œβ˜…")
}

func task3() {
    print("Task 3 μ‹œμž‘")
    sleep(4)
    print("Task 3 μ™„λ£Œβ˜…")
}

βœ”οΈ  μœ„ μ½”λ“œλ₯Ό λ™κΈ°λ‘œ μ‹€ν–‰ν•œ 경우

// 동기 μž‘μ—…
queue.sync {
	task1()
}
    
queue.sync {
	task2()
}
    
queue.sync {
	task3()
}

[μ‹€ν–‰ κ²°κ³Ό]

μ•„λž˜μ²˜λŸΌ μ½”λ“œκ°€ μˆœμ„œμ μœΌλ‘œ μžˆμ„ λ•Œ, 동기 μž‘μ—…κ³Ό λ™μΌν•œ κ²°κ³Όλ₯Ό μ–»λŠ”λ‹€.

task1()
task2()
task3()

Why?

iOSμ—μ„œ Main ThreadλŠ” 였직 ν•œκ°œλ§Œ μ‘΄μž¬ν•˜κ³  Main Queueμ—μ„œ μ‹€ν–‰λ˜λŠ”λ°,

이 Main QueueλŠ” Serial QueueλΌμ„œ ν•œλ²ˆμ— ν•œ 개의 Task밖에 μ‹€ν–‰ν•˜μ§€ λͺ»ν•˜κΈ° λ•Œλ¬Έμ— 순차적으둜 싀행됨.

βœ”οΈ μœ„ μ½”λ“œλ₯Ό λΉ„λ™κΈ°λ‘œ μ‹€ν–‰ν•œ 경우

// 비동기 μž‘μ—…
queue.async {
    task1()
}

queue.async {
    task2()
}

queue.async {
    task3()
}

[μ‹€ν–‰ κ²°κ³Ό]


πŸ”₯ Serial(직렬) 큐 VS Concurrency(λ™μ‹œ) 큐

큐의 νŠΉμ„±μ— 따라 κ΅¬λΆ„λœ 직렬과 λ™μ‹œλ‘œ λ‚˜λ‰˜μ–΄μ§

βœ”οΈ Main Queue - Serial(직렬) 큐 

  • Queueμ—μ„œ 받은 ν…ŒμŠ€ν¬λ“€μ„ 단 ν•˜λ‚˜μ˜ λ‹€λ₯Έ μ“°λ ˆλ“œλ‘œ λ³΄λ‚΄λŠ” 큐
  • >> ν•œ λ²ˆμ— ν•˜λ‚˜μ˜ ν…ŒμŠ€ν¬λ₯Ό μ‹€ν–‰ν•˜μ—¬ 순차적으둜 μ‹€ν–‰
  • μˆœμ„œκ°€ μ€‘μš”ν•œ μž‘μ—…μ„ μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©
  • Main Threadμ—μ„œ μ‚¬μš©λ˜λŠ” Queue(Main Queue)

Serial(직렬) 큐

βœ”οΈ Global Queue - Concurrency(λ™μ‹œ) 큐

  • Queueμ—μ„œ 받은 ν…ŒμŠ€ν¬λ“€μ„ μ—¬λŸ¬κ°œμ˜ λ‹€λ₯Έ μ“°λ ˆλ“œλ‘œ λ³΄λ‚΄λŠ” 큐 
  • >> λ§Žμ€ μž‘μ—…μ„ λ™μ‹œμ— μ‹€ν–‰
  • 각자 λ…λ¦½μ μ΄μ§€λ§Œ μœ μ‚¬ν•œ μ—¬λŸ¬κ°œμ˜ μž‘μ—…μ„ μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©

Concurrency(λ™μ‹œ) 큐

ex)

> μ…€ λ³„λ‘œ μœ μ‚¬ν•œ μž‘μ—…μ„ 각기 λ‹€λ₯Έ μ„œλ²„μ—μ„œ  μˆ˜ν–‰ν•˜λŠ” 처리

 

 

 

 

 

 

 

 

 

 

 

 

 

  • high, default, low, background λ„€ 가지 λ‹€λ₯Έ μš°μ„ μˆœμœ„λ₯Ό κ°€μ§ˆ 수 μžˆλ‹€.
  • qos(quality of service) class의 ν”„λ‘œνΌν‹°λ₯Ό μ§€μ •ν•˜μ—¬ μ‚¬μš©
let backgroundQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.background)

 

 

 

*ν•΄λ‹Ή λ‚΄μš©μ€ μΈν”„λŸ° κ°•μ˜ 보고 μ •λ¦¬ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.

λ°˜μ‘ν˜•