골치아픈 동시성 문제를 피하는 방법: 한 스레드만 자원에 접근 방식

이를 구현하기 위해서 ‘작업 큐’를 두는 것. 작업 요청 스레드를 여러개 두고 이것이 생산하는 작업들을 작업 큐에 넣는다. 그리고 이 큐에 쌓인거는 ‘상태 관리 스레드’에서만 가지고 처리하도록 해서 데이터 변경, 접근이 이 한 스레드만 할 수 있게하여 동시성 이슈를 사전에 차단.

while(running){
	//한 스레드만 큐에서 작업을 꺼내 실행한다.
	Job job = jobQueue.poll(1, TimeUnit.SECONDS);
	if(job == null){
		continue;
	}
	
	switch(job.getType()){
		case INC:
			...
			break;
			...
	}
}

switch 내부에는 한 스레드만 접근하기에 잠금 같은 수단 없어 코드가 단순해짐.

채널, 작업 큐 같은걸 스레드, 고루틴 사이에 두는 방식

Go 에는 “메모리를 공유하는 방식으로 (고루틴 간에) 소통말고 통신을 통해 메모리를 공유하라”는 말이 있다. Go 는 여러 고루틴이 동시 접근하는걸 제어하기 위한 잠금 장채 제공하지만, 그보다 채널을 통해 고루틴간 데이터를 공유하는 방식으로 동시성 구현을 권장한다.

참고로 논블로킹이나 비동기 IO를 사용하는 경우에는 블로킹 연산 최소화 해야하므로 단일 스레드 처리 방식이 적합함.

성능은?

단일 스레드 사용하면 교착 상태 같은 동시성 이슈 발생 하지않는 이점 발생. 그런데 다중 스레드가 동시에 처리하던걸 단일 스레드로 처리하게 되면 성능은? 동시에 실행하던 여러 작업을 순차적으로 하면 성능이 나빠지지 않나?

성능은 동시에 실행할 작업 개수와 임계 영역의 실행 시간에 따라 달라짐. 임계 영역의 실행 시간이 짧고 동시 접근하는 스레드 수가 적을 수록 잠금을 사용하는 구현의 성능 좋을 가능성 높음. 이 경우는 큐나 채널을 처리하는 데 드는 시간보다 잠금 획득, 해제에 드는 시간이 더 짧기 때문.

반면에;, 동시에 실행되는 작업 많고 임계영역의 실행시간이 길어진다면 큐나 채널 이용한 방식이 비슷하거나 더 나은 성능 낼 가능성 있음.