【发布时间】:2010-07-17 12:26:06
【问题描述】:
我有一个巨大的数组,其中包含一个结构“Tile”。我正在编写的程序是一个 2D 游戏,我不希望不同的玩家(由不同的线程处理)同时将他们的位置写入同一个图块,我想知道两件事。两个线程能否安全地同时写入数组中的两个不同位置,有没有什么有效的方法可以只锁定这个数组的一个索引?
【问题讨论】:
标签: c# multithreading arrays struct thread-safety
我有一个巨大的数组,其中包含一个结构“Tile”。我正在编写的程序是一个 2D 游戏,我不希望不同的玩家(由不同的线程处理)同时将他们的位置写入同一个图块,我想知道两件事。两个线程能否安全地同时写入数组中的两个不同位置,有没有什么有效的方法可以只锁定这个数组的一个索引?
【问题讨论】:
标签: c# multithreading arrays struct thread-safety
是的,您可以从不同的线程同时写入不同的位置。
要进行锁定,您应该创建一个锁数组,并使用一些简单的散列技术根据写入的位置选择一个锁。例如:
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 偷一个。
【讨论】:
i % numLocks就很简单了,但这只是特例。如果你有一个二维数组,你可以使用,例如,hash(i<<16 + j) % numLocks(其中hash 是任何好的整数散列函数)只要i < 0x10000。
您可以使用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 结构转换为类才能执行此操作。
【讨论】:
对于您想要做的事情没有内置支持。多个线程可以同时访问同一个数组,但是你需要自己通过同步来处理数据的一致性。因此,虽然可以实现每个索引锁定(这类似于数据库在事务中所做的),但我不确定这是否是您尝试做的正确方法。为什么要使用数组开头?
【讨论】:
我相信您可以使用lock statement 使代码线程安全,它正在访问数组并在线程之间共享。
【讨论】: