Console.WriteLine multithreaded output is empty with .NET, x64
-
20-03-2021 - |
Pregunta
There is a sample on MSDN for .NET ThreadPool. If I run this code, the output is completely erratic, sometimes, I get a completely empty output on the Console.
If I add a Thread.Sleep()
call, even just for a few ms, the output is fine.
AFAIK Console.WriteLine()
is thread-safe, so the output should always be there. But its not, at least not on my i7 2600 x64
compiled version. Obviously, if I add a breakpoint everything is fine, but it drives me mad.
I added a ConcurrentBag to make sure stuff is there, but even printing the elements of that is empty. Again, if I add a breakpoint everything is fine.
{
public class TaskInfo
{
public string m_text;
public int m_value;
public ConcurrentBag<int> m_bag;
public TaskInfo(string text, int value, ConcurrentBag<int> bag)
{
m_text = text;
m_value = value;
m_bag = bag;
}
}
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
void Run()
{
ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
for (int i = 0; i < 10; i++)
{
TaskInfo ti = new TaskInfo("Hello Thread", i, concurrentBag);
bool b = ThreadPool.QueueUserWorkItem(new WaitCallback(MyThreadFunction), ti);
if (!b)
{
Console.WriteLine("Damn!");
}
//Thread.Sleep(5);
}
for (int j = 0; j < concurrentBag.Count; j++)
{
Console.WriteLine("This is in the bag: {0}", concurrentBag.ElementAt(j));
}
}
static void MyThreadFunction(object stateInfo)
{
TaskInfo ti = (TaskInfo)stateInfo;
ti.m_bag.Add(ti.m_value);
Console.WriteLine(ti.m_text + ti.m_value.ToString());
}
}
}
Solución
I can very well understand that the output can be empty.
You push 10 jobs on a queue and then immediately start consuming the results. Not all jobs will have finished, and it could be none has even started yet.
And when running in the debugger, the program will terminate before you can see the WrtieLine()s from the MyThreadFunction.
Otros consejos
This is clearly a console application - after you have queued all of the work items, the Run
method returns and your program exits immediately. It will not wait for your workitems to finish. At the very least, add a Thread.Sleep
after the run to allow the threadpool to complete.
The correct thing to do is to use an array of ManualResetEvent
instances and wait for them to all be Set
by each worker thread. As this is a console application, you won't be able to use WaitHandle.WaitAll
unless you decorate your Main
method with [STAThread]
.
Isn't it that when you create threads, you have to join them (wait till they finish) until you try to print anything "produced" by them?
If not, you are relying on the scheduler which can switch threads before the last loop in Run
or not. If not, the last loop executes before any of your threads even start.
On the other hand, Thread.Sleep
lets the scheduler switch to other threads immediately, that's probably why you observe no issues with Thread.Sleep
inside.