경합 상황에서 동기화 방법으로 문제 해결하기
import UIKit
class ViewController: UIViewController {
    
    var value = 0
    
    let firstQueue = DispatchQueue(label: "First", attributes: .concurrent)
    let secondQueue =  DispatchQueue(label: "Second", attributes: .concurrent)
    
    let group = DispatchGroup()
    
    @IBOutlet weak var valueLabel: UILabel!
    
    @IBAction func start(_ sender: Any) {
        value = 0
        
        for _ in 1 ... 1000{
            firstQueue.async(group: group, execute: {
                self.value += 1
            })
            
            secondQueue.async(group: group, execute: {
                self.value += 1
            })
        }
        
        group.notify(queue: .main){
            self.valueLabel.text = "\(self.value)"
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
이러한 상황에서 우리는 2000이 출력될 것이라고 생각한다.
하지만 동기화가 되지 않았기 때문에 결과는 기대와는 다르게 나온다.

두 개의 스레드에서 동시에 동일한 값에 접근하면 결과는 알 수 없어진다.
위와 같은 문제를 Race Condition(경합 상황)이라고 한다.
그리고 Race Condition이 발생하는 범위를 Critical Section이라고 한다.
for _ in 1 ... 1000{
	firstQueue.async(group: group, execute: {
		self.value += 1 //Critical Section
	})
...
항상 똑같은 결과를 얻고 싶다면 속성을 동기화해야 한다.
sync를 사용하여 직렬화한다.let firstQueue = DispatchQueue(label: "First") //concurrent 사용해도 상관없음
let secondQueue =  DispatchQueue(label: "Second")
    
let syncQueue = DispatchQueue(label: "Sync")
@IBAction func start(_ sender: Any) {
        value = 0
        
        for _ in 1 ... 1000{
            firstQueue.async(group: group, execute: {
                self.syncQueue.sync { //sync로 직렬화
                    self.value += 1
                }
            })
            
            secondQueue.async(group: group, execute: {
                self.syncQueue.sync {
                    self.value += 1
                }
            })
        }
        
        group.notify(queue: .main){
            self.valueLabel.text = "\(self.value)"
        }
    }

Mutual Exclusion의 약자NSLock 클래스와 DispatchSemaphore로 구현 가능하다.
atomically라는 뜻은 1. 실행이 시작되면 중간에 중단할 수 없으며 중간에 다른 연산이 끼어들 수 없음, 2. 실행 결과가 다른 결과에 독립적임 을 의미한다.
lock: 이미 다른 스레드가 lock중이라면 unlock될때까지 대기한다. 따라서 이 메소드를 메인스레드에서 호출할 때는 조심해야 한다.unlock: 이전에 잠갔던 부분을 해제한다.
lock과 비슷한 역할을 하지만, 스레드를 블로킹하지 않는다는 특징이 있다.	 let lock = NSLock()
    @IBAction func mutex(_ sender: Any) {
        value = 0
        
        for _ in 1 ... 1000{
            firstQueue.async(group: group, execute: {
                self.lock.lock()
                self.value += 1
                self.lock.unlock()//lock한 스레드와 같은 스레드에서 unlcok 필수
                
            })
            
            secondQueue.async(group: group, execute: {
                self.lock.lock()
                self.value += 1
                self.lock.unlock()
            })
        }
        
        group.notify(queue: .main){
            self.valueLabel.text = "\(self.value)"
        }
    }

init에 전달하는 정수는 공유 리소스에 접근할 수 있는 스레드의 수를 전달count Value라고 부른다.
wait 메소드는 count 가 0보다 크다면 count value를 감소시키고 바로 리턴하고, 그렇지 않으면 0보다 큰 값이 될때까지 스레드를 블로킹하며 대기한다.signal 메소드는 count value를 증가시키므로 리소스의 사용이 끝났을 때 호출한다. 	let sem = DispatchSemaphore(value: 1)
    @IBAction func semaphore(_ sender: Any) {
        value = 0
        
        for _ in 1 ... 1000{
            firstQueue.async(group: group, execute: {
                self.sem.wait()
                self.value += 1
                self.sem.signal()
                
            })
            
            secondQueue.async(group: group, execute: {
                self.sem.wait()
                self.value += 1
                self.sem.signal()
            })
        }
        
        group.notify(queue: .main){
            self.valueLabel.text = "\(self.value)"
        }
    }