Go Receiver Race Condition

Golang Pointer Receiver를 사용할 때 발생할 수 있는 문제점
Receiver
Golang에서는 총 2가지의 receiver가 있습니다.
- Value Receiver
- Pointer Receiver
Pointer Receiver를 사용하게 될 경우, instance의 pointer에 접근이 가능하기 때문에, 원본 값의 데이터를 변경할 수 있기 때문에 POST/ UPDATE 같은 메서드의 경우 pointer receiver를 사용하는 것이 현명합니다.
|
|
이외에도 큰 struct의 경우, 복사본 대신 포인터를 전달하면 효율적이기 때문에 위와 같이 pointer receiver를 사용해서 불변일 값도 전달합니다. 그럼 value receiver를 언제 사용해야될까 고민해보겠습니다. 먼저 당연하게도 일반적인 type의 데이터이면서 modify가 필요없을 경우에 value receiver를 사용하면 될 것 같습니다.
추가로 이외에도 value receiver를 언제 사용하면 좋을지 개인적으로 생각해봤습니다.
Race condition on Pointer Receiver
생각하보면 pointer receiver는 특정 변수의 pointer를 전달하기 때문에 race condition이 발생할 수 있습니다.
만약 아래와 같은 Counter가 있다고 할때
|
|
1000개의 waitgroup에서 counter를 increment할 경우, 결과는 예측 불가합니다.
|
|
즉 pointer receiver는 당연하게도 race condition을 유발합니다. 이와 반대로 value receiver는 복사본을 넘기기 때문에 접근하는 go routine은 당연히 각각 복사된 value에 접근하기 때문에 race condition이 발생하지 않습니다.
Shallow Copy
기본적으로 golang의 struct는 Call by value이지만, 슬라이스, 맵, 채널, 함수등의 참조타입들을 Call by reference로 동작합니다.
slice가 call by reference가 될 수 밖에 없는 이유
참고로 Golang의 slice값은 pointer, length, capacity를 가진 struct입니다. 그렇기 때문에 slice를 복사하더라도 내부적으로 array에 대한 pointer는 여전히 같은 array를 가리키고 있기 때문에, slice를 복사하더라도, array는 여전히 원본을 가리키고 있는 shallow copy가 일어나게 됩니다.
|
|
다시 본론으로 돌아와서 Value Receiver를 사용하더라도 Referce type들을 사용할 경우 여전히 race condition이 발생할 수 있습니다.
|
|
Conclusion
이상으로 golang의 2가지 타입의 receiver를 살펴보았습니다. 정리하면, call by value인 작은 타입의 불변의 데이터인 경우 value receiver를 사용하고, 나머지의 경우 call by reference를 race condition을 조심하면서 사용하면 될 것 같습니다.