I have this simple code : (which i run in linqpad)
void Main()
{
for ( int i=0;i<10;i++)
{
int tmp=i;
new Thread (() =>doWork(tmp)).Start();
}
}
public void doWork( int h)
{
h.Dump();
}
the int tmp=i;
line is for capture variable - so each iteration will have its own value.
2 problems :
1) the numbers are not sequential , while thread execution is !
2) sometimes i get less than 10 numbers !
here are some executions outputs:
questions :
1) why case 1 is happening and how can i solve it ?
2) why case 2 is happening and how can i solve it ?
The nature of thread management is random. You can solve both task, but overhead is too big.
Threads execute in unpredictable order, and if the main thread finishes before others you'll not get all the numbers (dump() will not execute). If you mark your threads as IsBackground = false you'll get them all. There's no real solution for the first one except not using threads (or joining threads, which is same thing really).
You shouldn't expect any ordering between threads.
If you start a new thread, it is merely added to the operating system's management structures. Eventually the thread scheduler will come around and allocate a time slice for the thread. It may do this in a round-robin fashion, pick a random one, use some heuristics to determine which one looks most important (eg. one that owns a Window which is in the foreground) and so on.
If the order of the outputs is relevant, you can either sort it afterwards or - if you know the ordering before work begins already - use an array where each thread is given an index into which it should write its result.
Creating new threads the way your example does is also very slow. For micro tasks, using the thread pool is at least one order of magnitude faster.
It should not be expected that they are sequential. Each thread gets priority as the kernel chooses. It might happen that they look sequential, purely by the nature of when each is started, but that is pure chance.
For ensuring that they all complete - mark each new thread as
IsBackground = false
, so that it keeps the executable alive. For example:You can order thread execution, but it has to be done specifically by you for the specific problem with a specific solution.
You can use semaphores to achieve the behavior - search for block synchronization and mutual exclusion and Test-and-set method.
if ordering is important you may want to avail of a shared queue and use a semaphore to ensure only one thread operates on the top of the queue at a time