I have a Backgroundworker whose purpose is to run jobs sequentially in the background. Now one job is implemented in multithreading way. That mean, the Backgroundworker will create several threads. I use Task Parallel Library so I use the Task.Factory.StartNew to create multiple Tasks.
After the tasks are run, the Backgroundworker waits all of them to finish.
Now I print the Backgroundworker's ManagedThreadID and all the tasks' ManagedThreadIDs. I found that the BackgroundWorker's ManagedThreadID is always the same as the first task's ManagedThreadID. I think this shouldn't happen so I cannot explain. I think the Backgroundworker's thread must be different to all the tasks it creates so the ManagedThreadIDs must be all different from each other.
Can anyone explain why this scenario happens? Thank you very much.
Edit:
The code is similar to this:
Backgroundworker.Run(){
// Print Thread.CurrentThread.ManagedThreadID.
var task = Task.Factory.StartNew(action1); // action1, action2 also print ManagedThredID.
taskList.A开发者_StackOverflow中文版dd(task);
task = Task.Factory.StartNew(action2);
taskList.Add(task);
... // Several other tasks.
foreach(var task in taskList) task.Wait();
}
You will find that one task has the same ManagedThreadID as the Backgroundworker.
I would go on a limb here and guess that the TPL is smart enough to reuse the BackgroundWorker
thread. Since the worker waits for all tasks to complete running one task in the same thread is probably an optimization.
From further investigation, what you are seeing is a result of the expected behaviour of the Task.Wait
method. You can read more at Task.Wait and "Inlining" on the Parallel Programming Team blog.
If the Task being Wait’d on has already started execution, Wait has to block. However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.
The Background worker draws threads from the thread pool as well as TPL. What might happen is that the background worker is started, it draws a thread from the pool and fires TPL threads and returns immediately the thread to the pool. By the time TPL's first task is executed TPL draws a thread from the pool and it happens that it picks the same thread as the one once used by the background worker.
Of course that's just a supposition which cannot be verified because you haven't shown your code.
What you have stumbled upon is of course not a problem but a feature (optimization): The TPL is re-using threads as much as it can.
When you create a Task it is not immediately/permanently associated with a Thread. A Task is a Job that is put in a Queue and the Queue(s) are serviced by worker threads. So it could be that the Bgw Task is suspended and its thread returned to the pool, or more directly it could be done by the Wait():
// thread A
var t1 = Task.Startnew(...);
var t2 = Task.Startnew(...);
t1.Wait(); // Thread A is idle/available so Wait can execute t1
t2.Wait();
Use TaskCreationOptions.LongRunning to avoid re-cycling the background worker.
精彩评论