Skip to content

๐Ÿ” ๋™์‹œ์„ฑ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ LockManager ๋งŒ๋“ค๊ธฐ

๊น€์˜ํ˜„ edited this page Nov 23, 2024 · 1 revision

GameManager์—์„œ Map์„ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ Map์€ ๋™์‹œ์„ฑ ์ด์Šˆ์—์„œ ์ž์œ ๋กญ์ง€ ์•Š๋‹ค.

๊ทธ๋ž˜์„œ ๋™์‹œ์„ฑ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ž๋ฐ”์—์„œ๋Š” ConcurrentHashMap๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ๋…ธ๋“œ์—์„œ๋Š” ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์ง์ ‘ Customํ•ด์„œ ๋งŒ๋“ค์–ด์•ผ ํ•˜๊ณ  ์ง์ ‘ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

1. ์„ค์น˜

 npm install async-mutex
  • ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ํ”„๋กœ๋•ํŠธ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” async-mutex๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ์ƒ๊ฐํ–ˆ๋‹ค.

2. ๊ตฌํ˜„

import { Mutex } from 'async-mutex';

export class LockManager<K> {
  private locks = new Map<K, Mutex>();
  private globalMutex = new Mutex();

  private getLock(key: K): Mutex {
    let lock = this.locks.get(key);
    if (!lock) {
      lock = new Mutex();
      this.locks.set(key, lock);
    }
    return lock;
  }

  async withKeyLock<T>(key: K, callback: () => Promise<T>): Promise<T> {
    const lock = this.getLock(key);
    const release = await lock.acquire();
    try {
      return await callback();
    } finally {
      release();
    }
  }

  async withGlobalLock<T>(callback: () => Promise<T>): Promise<T> {
    const release = await this.globalMutex.acquire();
    try {
      return await callback();
    } finally {
      release();
    }
  }

  releaseKey(key: K): void {
    this.locks.delete(key);
  }

  async clear(): Promise<void> {
    return await this.withGlobalLock(async () => {
      this.locks.clear();
    });
  }
}
  • async-mutex์—์„œ ์ œ๊ณตํ•˜๋Š” Mutex ๋ฅผ ํ†ตํ•ด ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์ง„ํ–‰ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
  • lock.acquire ์„ ํ†ตํ•ด ๋ฝ์„ ํš๋“ํ•˜๊ณ , ํ•ด๋‹น ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋๋‚˜๋ฉด release ๋ฅผ ํ†ตํ•ด ๋ฝ์„ ๋ฐ˜๋‚ฉํ•˜๋Š” ํ˜•์‹์œผ๋กœ ์ง„ํ–‰๋œ๋‹ค.
  • ๊ฐ€์žฅ ์ค‘์š”ํ•œ private method withKeyLock
async withKeyLock<T>(key: K, callback: () => Promise<T>): Promise<T> {
  const lock = this.getLock(key);
  const release = await lock.acquire();
  try {
    return await callback();
  } finally {
    release();
  }
}
  • ๊ฐ ๊ฒŒ์ž„๋ฐฉ๋งˆ๋‹ค ๋ฝ์ด ์กด์žฌํ•˜๋ฉฐ ํ•ด๋‹น ๋ฝ์„ ํ†ตํ•ด ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • mutex.acquire๋ฅผ ํ†ตํ•ด ๋ฝ์„ ํš๋“ํ•˜๊ณ  ํ•ด๋‹น callbackํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ ํ›„ ๋‹ค์‹œ ๋ฝ์„ ๋ฐ˜๋‚ฉํ•˜๋Š” ํ˜•์‹์„ ์ง„ํ–‰ํ•œ๋‹ค.

3. ์‚ฌ์šฉ์‹œ

return await this.lockManager.withKeyLock(gameRoom.roomId, async () => {
      if (this.games.has(gameRoom.roomId)) {
        return;
      }

      const gameInfo = new Map<string, PlayerInfo>();
      players.forEach((role, client) => {
        gameInfo.set(client.nickname, { role, status: USER_STATUS.ALIVE });
        client.job = role;
        if (role === MAFIA_ROLE.MAFIA) {
          gameRoom.addMafia(client);
        }
      });
      this.games.set(gameRoom.roomId, gameInfo);
    });
  • lockManager.withKeyLock(gameRoom.roodId,๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ฐฉ์— ๋Œ€ํ•œ ๋ฝ์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ํ›„ ํ•ด๋‹นํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‹คํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰์ด ๋๋‚˜๋ฉด ๋‹ค์‹œ ๋ฝ์„ ๋ฐ˜๋‚ฉํ•˜์—ฌ ๋๋ƒ…๋‹ˆ๋‹ค.
  • lockManager ํ†ตํ•ด ๋™์‹œ์„ฑ ์ด์Šˆ์—์„œ ์ž์œ ๋กญ๊ฒŒ ๋˜์—ˆ๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด ์›์ž์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹œ์ž‘ ์•ž์—์„œ ๊ฒ€์ฆ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋™์‹œ์„ฑ ์ด์Šˆ์—์„œ ์ž์œ ๋กœ์›Œ์กŒ์Šต๋‹ˆ๋‹ค.

MafiaCamp

๐Ÿ“”์†Œ๊ฐœ
๐ŸŽฏํ”„๋กœ์ ํŠธ ๊ทœ์น™
๐Ÿ’ปํ”„๋กœ์ ํŠธ ๊ธฐํš
๐Ÿ€๊ธฐ์ˆ  ์Šคํƒ
๐Ÿ“š๊ทธ๋ฃน ํšŒ๊ณ 
๐ŸŒˆ๊ฐœ๋ฐœ ์ผ์ง€
๐Ÿ€๋ฌธ์ œ ํ•ด๊ฒฐ ๊ฒฝํ—˜
๐Ÿ”งํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…
Clone this wiki locally