Tech Off Thread

21 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

CSharp thread

Back to Forum: Tech Off
  • User profile image
    cro

    How can I thread this code if each unit is independent from others ?

     

    ... 
    foreach (Unit unit in units) 
    { 
       Compute(unit); 
    } 
    ...
    

  • User profile image
    wkempf

    Parellel.ForEach(units, unit => Compute(unit));

     

    That's actually overly verbose, but I didn't want to confuse anyone by not explicitly defining a lambda. This requires .NET 4, but you gave no requirements. I could accomplish the same thing in at least 3 other ways using .NET 2/3, and at least 4 other ways using .NET 4. The question is too vague, and the answers, quite frankly, too obvious. If you don't know how to create a thread, then you probably need a lot more knowledge about threading than just how to create one.

  • User profile image
    W3bbo

    Ugly and simple way: split the unit list into separate lists, one for each thread, then dispatch it off:

     

    	
    void ComputeUnits(Unit[] allUnits) {
    
    
    	int nofThreads = 2;
    
    	for(int thread=0;thread<nofThreads;thread++) {
    		
    	   	Unit[] threadsUnits = new Unit[ allUnits.Length / nofThreads ];
    		
    		Array.Copy( allUnits, thread * threadsUnits.Length, threadsUnits , 0, threadsUnits.Length );
    		
    		Thread t = new Thread( RunThread );
    		t.Start( threadsUnits );
      
    
    	}
    
    }
    
    
    void RunThread(Object arg) {
    
    	Unit[] units - (Unit[])arg;
    
    	foreach(Unit unit in units) Compute( unit );
    
    }

  • User profile image
    Minh

    Does Compute() use any shared state?

  • User profile image
    Minh

    W3bbo said:

    Ugly and simple way: split the unit list into separate lists, one for each thread, then dispatch it off:

     

    	
    void ComputeUnits(Unit[] allUnits) {
    
    
    	int nofThreads = 2;
    
    	for(int thread=0;thread<nofThreads;thread++) {
    		
    	   	Unit[] threadsUnits = new Unit[ allUnits.Length / nofThreads ];
    		
    		Array.Copy( allUnits, thread * threadsUnits.Length, threadsUnits , 0, threadsUnits.Length );
    		
    		Thread t = new Thread( RunThread );
    		t.Start( threadsUnits );
      
    
    	}
    
    }
    
    
    void RunThread(Object arg) {
    
    	Unit[] units - (Unit[])arg;
    
    	foreach(Unit unit in units) Compute( unit );
    
    }

    Also, W3bbo... this solution makes you decide important parallel issues at compile time... and it won't work if you # of work units is odd Smiley

  • User profile image
    W3bbo

    Minh said:
    W3bbo said:
    *snip*

    Also, W3bbo... this solution makes you decide important parallel issues at compile time... and it won't work if you # of work units is odd Smiley

    It should, since integer division truncates.

  • User profile image
    Minh

    W3bbo said:
    Minh said:
    *snip*

    It should, since integer division truncates.

    You sure it should? Smiley

     

    Take the case of allUnits.Length = 5 and thread = 1

  • User profile image
    spivonious

    W3bbo said:

    Ugly and simple way: split the unit list into separate lists, one for each thread, then dispatch it off:

     

    	
    void ComputeUnits(Unit[] allUnits) {
    
    
    	int nofThreads = 2;
    
    	for(int thread=0;thread<nofThreads;thread++) {
    		
    	   	Unit[] threadsUnits = new Unit[ allUnits.Length / nofThreads ];
    		
    		Array.Copy( allUnits, thread * threadsUnits.Length, threadsUnits , 0, threadsUnits.Length );
    		
    		Thread t = new Thread( RunThread );
    		t.Start( threadsUnits );
      
    
    	}
    
    }
    
    
    void RunThread(Object arg) {
    
    	Unit[] units - (Unit[])arg;
    
    	foreach(Unit unit in units) Compute( unit );
    
    }

    Why bother with the manual split? Wouldn't the thread pool do that for you?

  • User profile image
    wkempf

    spivonious said:
    W3bbo said:
    *snip*

    Why bother with the manual split? Wouldn't the thread pool do that for you?

    Yes, and much more efficiently.

  • User profile image
    W3bbo

    spivonious said:
    W3bbo said:
    *snip*

    Why bother with the manual split? Wouldn't the thread pool do that for you?

    I did say it was ugly Tongue Out

  • User profile image
    cro

    There is no share state. It must run also on Mono. The number of unit vary from 6 to 2000 with an average of 600 units. It is true that I don't have lots of knowledge on thread and on C#. I'm a C++/MFC developer and we are exploring the possibilities to move to C#.

  • User profile image
    Sven Groot

    cro said:

    There is no share state. It must run also on Mono. The number of unit vary from 6 to 2000 with an average of 600 units. It is true that I don't have lots of knowledge on thread and on C#. I'm a C++/MFC developer and we are exploring the possibilities to move to C#.

    Given those restrictions, this is probably what I'd do. It's similar to W3bbo's, but using the thread pool, avoiding the copy, and correctly dividing the number of items. Having multiple threads access the units array is fine as long as it's not modified for the duration of the operation.

     

    public static void ComputeUnits(Unit[] units)
    {
        int threadCount = Environment.ProcessorCount;
        int unitsPerThread = units.Length / threadCount;
        int remainder = units.Length % threadCount;
        for( int t = 0; t < threadCount; ++t )
        {
            int count = unitsPerThread;
            if( t == threadCount - 1 )
                count += remainder; // Give the last thread the remainder
    
            int start = t * unitsPerThread;
            ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count));
        }
    }
    private static void ComputeThread(Unit[] units, int start, int count)
    {
        for( int x = start; x < start + count; ++x )
            Compute(units[x]);
    }

  • User profile image
    cro

    Sven Groot said:
    cro said:
    *snip*

    Given those restrictions, this is probably what I'd do. It's similar to W3bbo's, but using the thread pool, avoiding the copy, and correctly dividing the number of items. Having multiple threads access the units array is fine as long as it's not modified for the duration of the operation.

     

    public static void ComputeUnits(Unit[] units)
    {
        int threadCount = Environment.ProcessorCount;
        int unitsPerThread = units.Length / threadCount;
        int remainder = units.Length % threadCount;
        for( int t = 0; t < threadCount; ++t )
        {
            int count = unitsPerThread;
            if( t == threadCount - 1 )
                count += remainder; // Give the last thread the remainder
    
            int start = t * unitsPerThread;
            ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count));
        }
    }
    private static void ComputeThread(Unit[] units, int start, int count)
    {
        for( int x = start; x < start + count; ++x )
            Compute(units[x]);
    }

    Is it possible to wait for all thread to be complete after the for in the ComputeUnits function ?

  • User profile image
    Sven Groot

    cro said:
    Sven Groot said:
    *snip*

    Is it possible to wait for all thread to be complete after the for in the ComputeUnits function ?

    I think you'll have to use wait handles (in this case I'm using ManualResetEvent which is the most appropriate here I think):

     

    public static void ComputeUnits(Unit[] units)
    {
        int threadCount = Environment.ProcessorCount;
        int unitsPerThread = units.Length / threadCount;
        int remainder = units.Length % threadCount;
        ManualResetEvent[] evts = new ManualResetEvent[threadCount];
    
        try
        {
            for( int t = 0; t < threadCount; ++t )
            {
                evts[t] = new ManualResetEvent(false);
    
                int count = unitsPerThread;
                if( t == threadCount - 1 )
                    count += remainder; // Give the last thread the remainder
    
                int start = t * unitsPerThread;
                ManualResetEvent evt = evts[t]
                ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evt));
            }
    
            WaitHandle.WaitAll(evts);
        }
        finally
        {
            foreach( ManualResetEvent evt in evts )
                evt.Dispose();
        }
    }
    
    private static void ComputeThread(Unit[] units, int start, int count, ManualResetEvent evt)
    {
        for( int x = start; x < start + count; ++x )
            Compute(units[x]);
        evt.Set();
    }

     

    Note: never, ever do this if ComputeUnits itself is on a thread pool thread. Waiting on a thread pool thread from another thread pool thread can cause deadlock.

  • User profile image
    cro

    Sven Groot said:
    cro said:
    *snip*

    I think you'll have to use wait handles (in this case I'm using ManualResetEvent which is the most appropriate here I think):

     

    public static void ComputeUnits(Unit[] units)
    {
        int threadCount = Environment.ProcessorCount;
        int unitsPerThread = units.Length / threadCount;
        int remainder = units.Length % threadCount;
        ManualResetEvent[] evts = new ManualResetEvent[threadCount];
    
        try
        {
            for( int t = 0; t < threadCount; ++t )
            {
                evts[t] = new ManualResetEvent(false);
    
                int count = unitsPerThread;
                if( t == threadCount - 1 )
                    count += remainder; // Give the last thread the remainder
    
                int start = t * unitsPerThread;
                ManualResetEvent evt = evts[t]
                ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evt));
            }
    
            WaitHandle.WaitAll(evts);
        }
        finally
        {
            foreach( ManualResetEvent evt in evts )
                evt.Dispose();
        }
    }
    
    private static void ComputeThread(Unit[] units, int start, int count, ManualResetEvent evt)
    {
        for( int x = start; x < start + count; ++x )
            Compute(units[x]);
        evt.Set();
    }

     

    Note: never, ever do this if ComputeUnits itself is on a thread pool thread. Waiting on a thread pool thread from another thread pool thread can cause deadlock.

    Really strange "Out of range exception" with t = 2 on this line "ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evts[t]));".

     

  • User profile image
    Dexter

    cro said:
    Sven Groot said:
    *snip*

    Really strange "Out of range exception" with t = 2 on this line "ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evts[t]));".

     

    Change:

     

    int start = t * unitsPerThread;
    ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evts[t]));

     

    to:

     

    int start = t * unitsPerThread;

    ManualResetEvent evt = evts[t];
    ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evt));

     

    That's because that lamba captures the variable t that changes before a compute thread has a chance to start.

     

  • User profile image
    Sven Groot

    Dexter said:
    cro said:
    *snip*

    Change:

     

    int start = t * unitsPerThread;
    ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evts[t]));

     

    to:

     

    int start = t * unitsPerThread;

    ManualResetEvent evt = evts[t];
    ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evt));

     

    That's because that lamba captures the variable t that changes before a compute thread has a chance to start.

     

    Ah crap you're right. I thought to do that for "start" but then I didn't remember it when doing the evts thing. Sad

  • User profile image
    spivonious

    Dexter said:
    cro said:
    *snip*

    Change:

     

    int start = t * unitsPerThread;
    ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evts[t]));

     

    to:

     

    int start = t * unitsPerThread;

    ManualResetEvent evt = evts[t];
    ThreadPool.QueueUserWorkItem(obj => ComputeThread(units, start, count, evt));

     

    That's because that lamba captures the variable t that changes before a compute thread has a chance to start.

     

    Ah nice catch. Threading can be really tricky.

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.