0

I am currently building a game using the Unity engine and C# in which i generate a gameworld. Currently it exists of a byte[,] of predetermined size, (between 512 and 4096, so for a total size of 4096x4096=16,7 million tiles). While implementing a multithreaded variant of a method which fills my array randomly with a 1 or a 2 dependent on a randomgenerator, i stumbled across a very weird problem.

Here is my code:

void CellularAutomata()
    {
        int ThreadCount = Environment.ProcessorCount; //Amount of cores. 4 in my case
        ThreadCount *= 4; // Multiplying by 4 to get higher amount of threads.
        int ThreadWorkSizeX = WorldProps.Size.x / ThreadCount; //Divide map x size by threadcount
        int ThreadWorkSizeY = WorldProps.Size.y / ThreadCount; // same for y
        Thread[] Threads = new Thread[ThreadCount]; //create simple array
        //Main thread nog mee laten werken
        for (int i = 0; i < ThreadCount; i++) //for loop to create threads
        {
            UnityEngine.Debug.Log(i); // 0
            Threads[i] = new Thread(() => {
                UnityEngine.Debug.Log("Thread says " + (i)); // i is 1???
                //Doing i-1 to correct this, but give unpredictable results.
                for (int x = ( (i-1) * ThreadWorkSizeX); x < (( (i-1) * ThreadWorkSizeX) + ThreadWorkSizeX); x++)
                {
                    for (int y = ( (i-1) * ThreadWorkSizeY); y < (( (i-1) * ThreadWorkSizeY) + ThreadWorkSizeY); y++)
                    {
                        if (WorldProps.RandomGen.Next(0, 100) < CellProps.FillPercentage)
                        {
                            TileTypeXZ[x, y] = 2;
                        }
                        else
                        {
                            TileTypeXZ[x, y] = 1;
                        }
                    }
                }
            });
            //Inserting a debug.log here affects the results?
            Threads[i].Start();
        }

        //Joining up again with my threads.
        //(Yes i know, main thread not really doing anything yet, but i want this working first.)
        for (int p = 0; p < ThreadCount; p++)
        {
            Threads[p].Join();
        }

    }

All this is done in this simple method. It currently obviously does not fully implement a Cellular Automata algorithm yet, but it has to in the future. I first wanted to get multithreaded random filling my map working. This whole process is fully contained in this method, and there are no external variables capable of affecting this method except my WorldProps object, which only houses a couple variables related to the size of the world and such. These ain't the problem here.

Therefore i wanted to split up my world, which is a two-dimensional array of bytes, and assign each thread a part of my world to fill. Here, however, i am stumbling upon some serious problems. First i also tried to manually start each thread after the for loop, but this resulted in i always being equal to the amount of threads (16 in my case), so this was a no go, but now there is this weird issue when i create a new Thread object, suddenly my 'i' variable is being incremented by one. Correcting this by using (i-1) does give me some results, if i modify the for loop, (so ignore the i-1 for now) but it stops working when using more than a single thread.

For some reason placing a Debug.Log ("Something"); on the specified line affects the results my threads are creating.

Sorry for my poor English, i don't yet master the language since i am just 17.

Either way,

Has anyone here any clue why i am having these weird problems with this for loop counter? Many thanks,

5
  • 2
    Are you trying to use multiple threads as an academic exercise, or because you think you need to? Also, why have 4 times more threads than you have cores available to run them? Commented Jul 25, 2015 at 18:58
  • albahari.com/threading Commented Jul 25, 2015 at 18:59
  • I use multithreading because generating all these values can take some time, especially on low powered devices. And to the 4 times as much threads, i am looking for the best core to thread ratio, so that was just some thing trying out. Commented Jul 25, 2015 at 19:19
  • Based on my napkin math, if you have say, a game world of width 3,000; and 16 threads that's 187.5 WorkThreadTasksX, 16 * 187 is 2992, that + 16 is 3008. So you'll go over? Or in some cases go very under? Commented Aug 9, 2020 at 0:35
  • As an addendum to my previous comment see here if you're like me and scratched their heads on this code: blogs.oracle.com/d/partitioning-work-over-multiple-threads Commented Aug 9, 2020 at 1:15

1 Answer 1

3

All threads share the same reference to i. Use a temporary variable

This is your problematic code

for(int i=0; i < 10; i++)
{
    new Thread(() => {
        Console.WriteLine(i);
    })
    .Start();
}

Change it to:

for(int i=0; i < 10; i++)
{
    var j = i;
    new Thread(() => {
        Console.WriteLine(j);
    })
    .Start();
}

For more info about closures : http://www.codethinked.com/c-closures-explained

Sign up to request clarification or add additional context in comments.

1 Comment

In the end it turned out that most of the randomness and the problems were caused by my random generator, since it was shared amongst all threads, and because the ordering of the threads is never certain in this case, it affected the results, even though i actually fixed the scope issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.