자바를 사용하면 꽤나 자주 사용하는 ConcurrentHashMap과 같은 Concurrent~ 류는 한 번에 여러 쓰레드가 접근하는 경우 데이터가 유실되는 상황을 피하기 위해 하나의 쓰레드만 접근할 수 있도록 한다.
자바스크립트가 구동되는 웹브라우저 환경은 애초에 싱글 쓰레드이기 때문에 이러한 설계가 필요 없으나, Promise를 기반으로 하는 병렬 처리가 많은 곳에서는 위와 같은 멀티쓰레드 환경과 같은 부작용이 발생하므로 이를 방지하기 위한 솔루션이 필요하다.
별도의 라이브러리가 있으면 좋겠지만, 라이브러리를 추가할 만한 환경이 아니고 그렇게 어려운 로직이 아니므로 직접 작성하기로 한다.
mutex.js
const _awaitUnlock = async (mutex) => {
// 잠금 상태가 아님 -> 즉시 resolve
if (!mutex._locked) {
return Promise.resolve()
}
return new Promise((resolve) => {
// 0.1초 후에 다시 확인
setTimeout(() => {
_awaitUnlock(mutex).then(() => resolve())
}, 100)
})
}
class Mutex {
constructor() {
this._locked = false
}
async lock() {
// 잠금 상태가 풀릴 때 까지 대기
await _awaitUnlock(this)
this._locked = true
}
// 잠금 해제는 별도의 제약을 주지 않음.
release() {
this._locked = false
}
}
export default Mutex
사용 예시
const mutex = new Mutex()
const parallelLogic = async () => {
await mutex.lock() // 여기서 mutex가 유휴 상태일 때 까지 대기하며, 유휴 상태가 되면 잠금을 걸고 진행한다.
try {
// ..비즈니스 로직..
} finally {
mutex.release() // 반드시 실행한다. 그렇지 않으면 Dead-lock 발생함.
}
}