views:

70

answers:

4

Hi

I have an huge array which contains a struct "Tile". The program im writing is a 2D game, and I don't want different players (handled by different threads) to write their position to the same tile at the same time, and I wondered two things. Can two threads write to two different places in the array at the same time safely, and is there some effective way to lock only one index of this array?

+1  A: 

Yes, you can write to different positions simultaneously from different threads.

To do the locking, you should create an array of locks, and use some simple hashing technique to choose a lock, based on the position being written. For instance:

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;
        }
    }
}

This avoids the need to create zillions of locks, but still keeps lock-contention to a minimum. You can set numLocks up or down, based on profiling. Keep it a power of two, though, for an efficient modulo computation.

One final minutiae: beware of aliasing effects. For instance, multiple-of-16 positions might happen to be very popular with your threads for some odd reason, in which case, contention will go through the roof. If this is the case, you'll need a stronger hash. Perhaps (uint)i.GetHashCode() % numLocks will do, but I'm not sure what Int32.GetHashCode does; it might just return the number itself. Failing this, you can steal one from Bob Jenkins.

Marcelo Cantos
Ah, thanks:)Thought about having an equally sized array of objects to lock with, but this makes more sense.
Alle
Doing it that way means I have to change to a 1-Dimensional array though, right?
Alle
@Alle: the principle is to produce a hash of the position. If it's a 1-D array, then it's a simple matter of `i % numLocks`, but that's just a special case. If you have a 2-D array, you could use, e.g., `hash(i<<16 + j) % numLocks` (where `hash` is any good integer hash function) as long as `i < 0x10000`.
Marcelo Cantos
Ok, it will probably take me some time to fully understand this, I haven't tried to implement it yet as I have other problems to solve first, but it definently seems to be a good solution if it works in 2D.
Alle
A: 

There is no built-in support for what you want to do. Multiple threads can access the same array at the same time, but you'd need to take care of data consistency yourself by means of synchronization or so. Therefore, while it is possible to implement per-index locking (which is similar to what a database does in a transaction), I'm not sure that this is the right approach for what you're trying to do. Why are you using an array to start with?

Lucero
I think it is supposed to have fast access which I need, and since it is a 2D game, I can use the X and Y coordinates as indexes.
Alle
A: 

I believe you can use the lock statement to make the code thread safe which is accessing the array and is shared between the threads.

Imran S.
I tried that but I think it said that Struct aren't a reference type or something like that
Alle
Also this does not protect the array indices, which may not be what the question asks for.
Lucero
+1  A: 

You can use the Interlocked.CompareExchange function to do the read and write safely without the explicit use of locks.

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]); }
  }
}

Of course you will have to convert your Tile struct to a class to be able to do this.

Brian Gideon