If you create a background Thread
and immediately call Join
after running it, you basically just wasted time and memory for creating a synchronous method, because your current thread will block until the background thread is finished. If the current thread is a UI thread, this will be pretty obvious.
Also, using Thread.Abort
to kill a thread is not recommended.
I would suggest creating a long-lived background thread which will most of the time wait for a signal from the main thread. This will ensure that you don't unnecessarily create multiple threads in case you are receiving more requests than your worker method can handle.
This is the general idea:
// have a long lived and prosperous thread which handles jobs
private readonly Thread _backgroundWorker;
// you need a way to signal the thread to continue running
private readonly AutoResetEvent _signalNewTask;
// you need a flag indicating you want to stop (to avoid aborting the thread)
private volatile bool _keepRunning;
// and you need to pass the current job to that thread
private volatile Job _currentJob;
The loop should look something like this:
// this runs on a background thread
private void WorkerLoop()
{
Job lastJob = null; Image lastResult = null;
while (_keepRunning)
{
// use an AutoResetEvent for cross-thread signalization
_signalNewTask.WaitOne();
// make sure the app isn't ending
if (!_keepRunning)
break;
// don't bother if we already processed this job
if (lastJob == _currentJob)
continue;
// capture the job in a local variable
lastJob = _currentJob;
// long processing
lastResult = LoadImage(lastJob);
// check if this is still the last requested job
if (_keepRunning && lastJob == _currentJob)
DisplayImage(lastResult);
}
}
To schedule a job for execution, you simply set the field and signal the event:
private void ScheduleNewJob(Job nextJob)
{
// don't waste time if not needed
if (nextJob == _currentJob)
return;
_picLoadingJobCard.Visible = true;
_currentJob = nextJob;
_signalNewTask.Set();
}
You'll also need to add initialization and cleanup code to your Form
:
public SomeForm()
{
InitializeComponent();
_keepRunning = true;
_signalNewTask = new AutoResetEvent(false);
_backgroundWorker = new Thread(WorkerLoop);
_backgroundWorker.IsBackground = true;
_backgroundWorker.Priority = ThreadPriority.BelowNormal;
_backgroundWorker.Start();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
// set the running flag to false and signal the thread
// to wake it up
_keepRunning = false;
_signalNewTask.Set();
// this will lock very shortly because the background
// thread breaks when the flag is set
_backgroundWorker.Join();
base.OnFormClosed(e);
}
And since DisplayImage
(or whatever) will be called from a background thread, you have to schedule on the UI thread by calling Invoke
:
private void DisplayImage(Image result)
{
if (this.InvokeRequired)
{
Invoke(new Action<Image>(DisplayImage), result);
return;
}
_picLoadingJobCard.Visible = false;
_picJobCard.Image = result;
}