I am developing a multi-threaded Serial Port communication software, and I noticed that a ContextSwitchDeadlock
is thrown while using the below provided code.
Note that, appending GetPortNames()
to its own function and then writing out the IDs of each thread (Start and End) to the Console shows that every started thread is also correctly, as seen in the Explicit Example.
Simple Example:
while (true)
{
Task.Run(() => SerialPort.GetPortNames());
Thread.Sleep(100);
}
Explicit Example:
[STAThread]
private static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
Task.Run(() => Example());
Thread.Sleep(100);
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
}
private static void Example()
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
SerialPort.GetPortNames();
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
String Compare Example (NO EXCEPTION):
[STAThread]
private static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
Task.Run(() => Example());
Thread.Sleep(100);
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
}
private static void Example()
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
string.Compare("a", "b");
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
As I do not think that any threads are being deadlocked, this error should not happen. The full error message:
Managed Debugging Assistant 'ContextSwitchDeadlock' : 'The CLR has been unable to transition from COM context 0xb434c0 to COM context 0xb43408 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.
I am curious to understand why is it happening.
The simple example provided shows the simplest way to achieve this, while the explicit example shows a way to track all threads, the output of which is:
Start 1
Start 3
End 3
End 1
Start 1
Start 3
End 3
End 1
Start 1
Start 4
End 4
End 1
Start 1
Start 3
End 3
End 1
And as such it can be seen that no Thread is deadlocked, as they all start and end correctly (although I have no idea where Thread No.2 is).