싱글 스레드로 동시성 문제 방지

  • 골치아픈 동시성 문제를 피하는 방법: 한 스레드만 자원에 접근 방식
  • 이를 구현하기 위해서 '작업 큐'를 두는 것.
  • 작업 요청 스레드를 여러개 두고 이것이 생산하는 작업들을 작업 큐에 넣는다.
  • 그리고 이 큐에 쌓인거는 '상태 관리 스레드'에서만 가지고 처리하도록 해서 데이터 변경, 접근이 이 한 스레드만 할 수 있게하여 동시성 이슈를 사전에 차단.
    • 이 큐를 처리하는 스레드는 하나만 하자는걸로 이해하면 되는건가?
while(running){
	//한 스레드만 큐에서 작업을 꺼내 실행한다.
	Job job = jobQueue.poll(1, TimeUnit.SECONDS);
	if(job == null){
		continue;
	}
	
	switch(job.getType()){
		case INC:
			...
			break;
			...
	}
}
  • switch 내부에는 한 스레드만 접근하기에 잠금 같은 수단 없어 코드가 단순해짐.
  • 채널, 작업 큐 같은걸 스레드, 고루틴 사이에 두는 방식
  • Go 에는 "메모리를 공유하는 방식으로 (고루틴 간에) 소통말고 통신을 통해 메모리를 공유하라"는 말이 있다.
  • Go 는 여러 고루틴이 동시 접근하는걸 제어하기 위한 잠금 장치 제공하지만, 그보다 채널을 통해 고루틴간 데이터를 공유하는 방식으로 동시성 구현을 권장한다.
    • 여러 채널두고 각 채널 담당 스레드를 두는 방식인가?
  • 참고로 논블로킹이나 비동기 IO를 사용하는 경우에는 블로킹 연산 최소화 해야하므로 단일 스레드 처리 방식이 적합함.
    • 단일 스레드랑 블로킹 연산 최소화가 뭔 상관이지?

성능은?

  • 단일 스레드 사용하면 교착 상태 같은 동시성 이슈 발생 하지않는 이점 발생.
  • 그런데 다중 스레드가 동시에 처리하던걸 단일 스레드로 처리하게 되면 성능은?
  • 동시에 실행하던 여러 작업을 순차적으로 하면 성능이 나빠지지 않나?
  • 성능은 동시에 실행할 작업 개수와 임계 영역의 실행 시간에 따라 달라짐.
  • 임계 영역의 실행 시간이 짧고 동시 접근하는 스레드 수가 적을 수록 잠금을 사용하는 구현의 성능 좋을 가능성 높음.
    • 이 경우는 큐나 채널을 처리하는 데 드는 시간보다 잠금 획득, 해제에 드는 시간이 더 짧기 때문.
  • 반면에, 동시에 실행되는 작업 많고 임계영역의 실행시간이 길어진다면 큐나 채널 이용한 방식이 비슷하거나 더 나은 성능 낼 가능성 있음.