How can I thread this code if each unit is independent from others ?
...
foreach (Unit unit in units)
{
Compute(unit);
}
...
Loading User Information from Channel 9
Something went wrong getting user information from Channel 9
Loading User Information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
How can I thread this code if each unit is independent from others ?
...
foreach (Unit unit in units)
{
Compute(unit);
}
...
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.
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 );
}
Does Compute() use any shared state?
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 ![]()
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
It should, since integer division truncates.
W3bbo said:Minh said:*snip*It should, since integer division truncates.
You sure it should? ![]()
Take the case of allUnits.Length = 5 and thread = 1
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?
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.
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 ![]()
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#.
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]);
}
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 ?
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.
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]));".
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.
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. ![]()
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.
Thread Closed
This thread is kinda stale and has been closed but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.