My question may not really be about realtime processing, but then again, it may be.
My application has several threads that are far more important than the GUI, BUT, I do want the GUI to at least be usable. I don't want it locked at all times, and I do want to update the screen given results of processing I am performing.
Currently all my essential items are isolated in separate threads, and I call a delegate to my GUI to display results.
My GUI works, but if I change tabs, or minimize/maximize it, it has been known to hinder my other threads to the point where they can not perform their operations within the 0.1s time limit they are held to.
This is what I am doing to call my delegate:
delegate void FuncDelegate(ResultContainer Result);
FuncDelegate DelegatedDisplay= new FuncDelegate(DisplayResults);
//then later on
Invoke(DelegatedDisplay, Result);
Most of my critical processes are threads that run in continuous loops, pulling from and pushing to various buffers (ArrayLists and normal Lists).
One of my critical threads is launched every time, using:
Thread mythread = new Thread(new ThreadStart(ProcessResults));
mythread.Start();
The reason I thought to do this, instead of just having a thread running in a loop, pulling from lists, is that I thought perhaps the reason I was running out of clock time was that I have a polling loop that I worry consumes too many resources (although I am using Thread.Sleep(5) every time the poll turns up negative).
Does launching a new thread each time I need the concurrent process cost me valuable time? Should this be a loop? Are my loops to blame?
Can I give a thread higher priority than others, or is the use of Thread.Sleep my only option? If I do assign higher thread priorities, how can I be sure other threads can even survive?
Why are simple forms events hampering my other threads so much? Is there a way to give my GUI thread an assigned, lower amount of resources? Can I use a Thread.Sleep somehow to block Form events if the other threads are running out of clock time?
Short of an answer to all my frustrating questions, is there some kind of thread profiler I can be using to help figure my mess out? I tried using "Managed Stack Explorer" but somehow that doesn't always show what threads my application has.
Any help on this matter would help me greatly.
Well here's a start:
Invoke(DelegatedDisplay, Result);
This means that you are causing the background thread to wait until the UI thread actually performs the drawing operation, then proceed. From the thread's point of view that's an eternity. You might want to investigate asynchronous updates to the UI:
BeginInvoke(DelegatedDisplay, Result);
That's equivalent to telling the UI thread "when you have a chance, perform this drawing action", then proceeding with the work that you were doing.
You should be aware that this may cause thread-safety issues that didn't occur using Invoke
, though. For example, if the background thread is still modifying Result
while the UI is attempting to draw, you might have unexpected race conditions.
See Control.Invoke vs Control.BeginInvoke
Using marshaling techniques like Invoke
and BeginInvoke
to update the UI is part of the problem. In fact, I rarely use marshaling operations for the UI and worker thread interactions because it is not that great of a solution. Well, to be frank, it can be (and usually is) the worst solution in most cases of this nature.
What I typically do is have the worker thread publish its results or progress to a shared data structure and have the UI thread poll for it using a System.Windows.Forms.Timer
(or DispatcherTimer
) on an interval that is tuned to work best for the situation at hand.
Here is what it might look like.
public class YourForm : Form
{
private ConcurrentQueue<ResultContainer> results = new ConcurrentQueue<ResultContainer>();
public UpdateTimer_Tick(object sender, EventArgs args)
{
// Limit the number of results to be processed on each cycle so that
// UI does not stall for too long.
int maximumResultsToProcessInThisBatch = 100;
ResultContainer result;
for (int i = 0; i < maximumResultsToProcessInThisBatch; i++)
{
if (!results.TryDequeue(out result)) break;
UpdateUiControlsHere(result);
}
}
private void WorkerThread()
{
while (true)
{
// Do work here.
var result = new ResultContainer();
result.Item1 = /* whatever */;
result.Item2 = /* whatever */;
// Now publish the result.
results.Enqueue(result);
}
}
}
The thing is, people have been so programmed to automatically use Invoke
or BeginInvoke
to update the UI that they ignore better solutions. It has gotten to the point where these marshaling techniques fit into the realm of cargo cult programming. I probably sound like a broken record regarding this topic because I rip on it all of the time. The technique I used above has the following advantages.
Invoke
.BeginInvoke
.Invoke
or BeginInvoke
calls.Does launching a new thread each time I need the concurrent process cost me valuable time? Should this be a loop? Are my loops to blame?
I would avoid creating threads willy-nilly. If you can keep the thread running in a loop that would be better.
Can I give a thread higher priority than others, or is the use of Thread.Sleep my only option? If I do assign higher thread priorities, how can I be sure other threads can even survive?
Giving your worker thread a higher priority would probably help in this case. Thread.Sleep(5)
will not sleep for 5ms though. It just does not work that way. By the way there are some special values that you can pass to Thread.Sleep
.
Why are simple forms events hampering my other threads so much? Is there a way to give my GUI thread an assigned, lower amount of resources? Can I use a Thread.Sleep somehow to block Form events if the other threads are running out of clock time?
It is because you are using Invoke
. Avoiding the marshaling operations will help significantly since it decouples the threads. Do not use Thread.Sleep
on the UI thread. The UI thread must remain unblocked for it to work properly. If you use the solution I propose above then it is much easier to throttle the UI thread.
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