【问题标题】:Array of structs, multithreading, can I write at the same time to different indexes?结构数组,多线程,我可以同时写入不同的索引吗?
【发布时间】:2010-07-17 12:26:06
【问题描述】:

我有一个巨大的数组,其中包含一个结构“Tile”。我正在编写的程序是一个 2D 游戏,我不希望不同的玩家(由不同的线程处理)同时将他们的位置写入同一个图块,我想知道两件事。两个线程能否安全地同时写入数组中的两个不同位置,有没有什么有效的方法可以只锁定这个数组的一个索引?

【问题讨论】:

    标签: c# multithreading arrays struct thread-safety


    【解决方案1】:

    是的,您可以从不同的线程同时写入不同的位置。

    要进行锁定,您应该创建一个锁数组,并使用一些简单的散列技术根据写入的位置选择一个锁。例如:

    class TileArray
    {
        private static readonly int numLocks = 16;
        private object[] locks = (from i in Range(0, numLocks) select new object()).ToArray();
        private Tile[] tiles = hugeTileArray();
    
        ...
    
        public Tile this[int i]
        {
            get { return tiles[i]; }
            set
            {
                lock (locks[i % numLocks])
                    tiles[i] = value;
            }
        }
    }
    

    这避免了创建无数锁的需要,但仍将锁争用降至最低。您可以根据分析设置 numLocks 向上或向下。不过,为了高效的模计算,请保持它的 2 次方。

    最后一个细节:提防混叠效应。例如,由于某种奇怪的原因,16 个位置的倍数可能碰巧在您的线程中非常受欢迎,在这种情况下,争用将达到顶峰。如果是这种情况,您将需要更强的哈希。也许(uint)i.GetHashCode() % numLocks 会做,但我不确定Int32.GetHashCode 会做什么;它可能只是返回数字本身。如果做不到这一点,你可以从Bob Jenkins 偷一个。

    【讨论】:

    • 啊,谢谢 :) 想过要锁定一个大小相同的对象数组,但这更有意义。
    • 这样做意味着我必须更改为一维数组,对吧?
    • @Alle:原理是产生位置的哈希。如果是一维数组,那么i % numLocks就很简单了,但这只是特例。如果你有一个二维数组,你可以使用,例如,hash(i<<16 + j) % numLocks(其中hash 是任何好的整数散列函数)只要i < 0x10000
    • 好的,我可能需要一些时间才能完全理解这一点,我还没有尝试实现它,因为我还有其他问题需要先解决,但如果它适用于 2D。
    • 这在游戏之外还有很多用途。您创建锁定对象数组的 LINQ 语句非常紧凑。
    【解决方案2】:

    您可以使用Interlocked.CompareExchange 函数安全地进行读写操作,而无需显式使用锁。

    public class Example
    {
      private Tile[] m_Array;
    
      public Tile this[int index]
      {
        get { return Interlocked.CompareExchange(ref m_Array[i], null, null); }
        set { Interlocked.CompareExchange(ref m_Array[i], value, m_Array[i]); }
      }
    }
    

    当然,您必须将 Tile 结构转换为类才能执行此操作。

    【讨论】:

      【解决方案3】:

      对于您想要做的事情没有内置支持。多个线程可以同时访问同一个数组,但是你需要自己通过同步来处理数据的一致性。因此,虽然可以实现每个索引锁定(这类似于数据库在事务中所做的),但我不确定这是否是您尝试做的正确方法。为什么要使用数组开头?

      【讨论】:

      • 我觉得它应该有我需要的快速访问,因为它是一个2D游戏,我可以使用X和Y坐标作为索引。
      【解决方案4】:

      我相信您可以使用lock statement 使代码线程安全,它正在访问数组并在线程之间共享。

      【讨论】:

      • 我试过了,但我认为它说 Struct 不是引用类型或类似的东西
      • 这也不保护数组索引,这可能不是问题所要求的。
      猜你喜欢
      • 1970-01-01
      • 2011-08-02
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多