I am about to write an application that gets streaming data from the TCP port, doing some real time calculations on them. All good so far, but the user interface and the marquee progress bar are getting stuck (for irregular, short time periods) when the producer thread is started to do some packaging on the data chunks (see code).
void Produce()
{
try
{
while (true)
{
foreach (Chunk _chunk in bcPort)
{
if (_ThreadCanceler.IsCancellationRequested) break;
Chunk chunk = bcPort.Take();
chunk.TimeTracker = new Stopwatch();
chunk.SegmentId = iSegmentId;
if (chunk.Channel + 1 == iChannels) iSegmentId++; // last channel, raise segment id.
iPrevChannel = chunk.Channel;
// _ProcessAndJoin.EnqueueTask(chunk, _ThreadCanceler);
iChunksProduced++;
_LogWriter.WriteMessage("Task " + Task.CurrentId.ToString() + "(producer): ADDED_ Chunk[" + chunk.Channel + ":" + chunk.Vals.Count.ToString() + ":" + chunk.SegmentId + "] [" + iChunksProduced + "]. " + bcPort.Count + " for takeaway. Thread: " + Thread.CurrentThread.ManagedThreadId.ToString());
}
if (_ThreadCanceler.IsCancellationRequested) break;
}
}
catch (Exception ex)
{
Console.WriteLine("ForkAndCrate.cs: Produce(): " + ex.ToString());
}
}
I did a lot of testing and found out that the access to the BlockingCollection bcPort seems to be the problem. bcPort gets constantly chunks from another data-adder thread which is also supposed not to influence the ui thread. So I don't understand the following:
1.) Why is the GUI getting stuck when I am using different threads for adding and packaging the chunks?
2.) Why is this happening when I am using a BC for storing data? Aren't these thread safe collections for this certain purpose?
By the way: Windows 7 ResourceManager shows 100% CPU usage during streaming, the chunks contain about 2000 float values each, 4 or 5 of them a rushing in per second. I also disabled the Logger but no effect. The consumer and evaluation threads are disabled.
Besides the ui thread, there is only a thread called "ReceiveAndSave" which makes chunks out of incoming float values (see code, Method "Add"). the thread "Producer" is doing some further packaging and enqueues the chunks for the consumer (deactivated).
public void Add(short iChannel, float fValue)
{
try
{
_Benchmark.UpdateRec(); // one value received, update benchmark:
if (!cdBasin[iChannel].Enqueue(fValue))
{
Chunk chunk = new Chunk();
chunk.Vals = cdBasin[iChannel].ToListDeep;
chunk.Channel = iChannel;
bcPort.Add(chunk);
cdBasin.AddOrUpdate(iChannel, new BoundedQueue<float>(iSegmentSizePerChannel), (key, oldValue) => new BoundedQueue<float>(iSegmentSizePerChannel));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Stop", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
The producer is started with 'myThreads[0] = new Thread(Produce); myThreads[0].Name = "Produce"; myThreads[0].Start();'
It doesn't really get stuck, you are just giving it too much work to do. Which makes it not take care of its low-priority tasks anymore, responding to input and paint notifications. Which indeed makes it look like it is stuck, your user will certainly think so.
The key is to only update the UI at a rate that makes sense to the human eye. Which cannot perceive anything but a blur beyond 25 updates per second. If you push it to as many as a thousand updates per second you'll get the behavior you describe. So collect results and don't invoke/update until enough time has expired. And in general compact the info you display into consumable chunks to give the user meaningful feedback, a list that displays hundreds of new items a second is not useful to anybody.