I've run into an interesting problem in my C# .Net 4.0 application using the SerialPort
class and either ThreadPool.QueueUserWorkItem
or Task
s.
The problem only occurs if I use 2 or more SerialPorts simultaneously. Each serial port runs in its own thread that I create in 1 of 3 ways:
new Thread(DoSerialCommX)
ThreadPool.QueueUserWorkItem(DoSerialCommX)
new Task(DoSerialCommX, TaskCreationOptions.LongRunning).Start()
To illustrate the problem, I created my DoSerialCommX
method to read and write to the serial port forever in a loop. It looks something like this: (I'm not actually doing this in my real program. This is just a snippet from my test program that isolates and illustrates the problem).
private void DoSerialCommX()
{
SerialPort port = new SerialPort("ComX", 9600);
port.Open();
while(true)
{
//Read and write to serial port
}
}
If I use either method 2 or 3, the serial communication stutters and I receive many communication timeouts. If I use method 1, all is good. Also, I should mention this only seems to occur on my Intel Atom based PCs. Desktop PC's seem to have no problems.
I know the thread pool reuses threads, and by default Task
uses the thread pool. And I know that the thread pool really intended for short-lived operations. But I tried using the TaskCreationOptions.LongRunning
, which I thought would spawn a dedicated thread rather than use the thread pool, but it still didn't work.
So the Question: What makes Thread
so special in this situation? Is there something about Thread
that makes it better suited for IO operations?
Edit:
The answers so far seem to assume that I am trying to use the ThreadPool or Tasks for a never-ending process. In my real application this is not the case. I'm only using a never-ending loop in the code above to illustrate the problem. I really need to know why Thread
works and ThreadPool
and Task
don't. What is technically different about them that would cause the serial communication to hiccup?
Philosophically there is little difference between 1, 2 and 3 in how executing thread behaves. They all share the same default execution priority unless you override it - notionally the thread scheduler would use the same strategy to schedule them. They'd all sit happily spinning in the loop.
I suspect the bigger difference between methods is:
From a functional point of view use of Method 2 and 3 are somewhat abusive - your intention is to never exit the method. These strategies are optimized for atomic, finite operations, and somewhat unguided execution oft suited for IO Completion port tasks such as async network, disk operations etc... (Maybe a possibility for your serial port code too?)
I would skip 2 & 3 unless you're interested in adapting to Async IO. Focus on thread - seems like you're wanting finer grain, predictable execution control without the infrastructure overhead that Threadpool brings.
There is something off with either your code, your driver or your hardware that has put your serial port performance so 'on the edge' that it is on the verge of not working all the time. There should be no problem using a dedicated thread or the threadpool, (mod. using up a threadpool thread to block on a serial port read).
Two 9600-baud serial ports you can run with an abacus, providing the beads and wires are well oiled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With