首页 > 解决方案 > 使我的代码线程安全时遇到问题

问题描述

CreateNewRound() 方法在运行时被多个线程访问。假设 _game.CurrentRound = 99 并且两个线程同时访问该方法,它们都将 currentRoundId 初始化为 100,并且两个线程都添加了两个具有相同 roundId 的实体。但这是错误的,我不希望这种情况发生,因为回合应该是独特的和不同的。我该如何解决这个问题,以便线程一个添加一个第 100 轮的实体,另一个添加第 101 轮的实体。


public void CreateNewRound()
{
   var game = _cache.GetGameById(_session.gameId);
   var currentRoundId = game.CurrentRound + 1;

   var response = SomeAPI.SomeCall();
   if (response.responseCode == (int)responseCodes.Success)
   { 
      _dbContext.GameState.Add(new GameState() { RoundId = CurrentRoundId });
      _dbContext.SaveChanges();
   }
}

标签: c#.netmultithreadingthread-safety

解决方案


如果(且仅当)所有调用的方法都是纯的,即结果仅取决于输入参数,您可以简单地使用 interlocked.Increment 来确保 currentRound 对于每个调用都是唯一的:

    private int currentRound = 0;
    public void CreateNewRound()
    {
        var thisRound = Interlocked.Increment(ref currentRound);
        var gamestate = CreateGameState(thisRound)
        // process game state
    }

在大多数游戏中,下一轮将取决于上一轮的游戏状态。在这种情况下,您必须按顺序运行每一轮。典型的解决方案是为此使用锁:

    private int currentRound = 0;
    private object myLock = new object();
    private MyGameState gameState;
    public void CreateNewRound()
    {
        lock (myLock)
        {
            currentRound++;
            gameState = ComputeNextGameState(gameState, currentRound);
            // process game state
        }
     }

还有其他选择,例如分配一个特定线程来执行所有游戏状态更新,并且CreateNewRound只要求更新线程进行更新。


推荐阅读