I am doing a practise GUI Oven program using a thread, I am not sure if I should even be doing this because I want to interact with the GUI when the Heating process is ongoing. When I try to abort the thread by click btnStop_Click, it throws the NullReference exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Please advice on how can I gracefully stop the thread. Thanks.
Code:
public partial class Form1 : Form
{
private Thread t;
public Form1()
{
InitializeComponent();
}
// button to begin heating
private void btnStart_Click(object sender, EventArgs e)
{
if ((txtMin.Text) == "" || (txtSec.Text) == "")
{
MessageBox.Show("Please enter duration of heating");
}
else
{
t = new Thread(heatIt);
btnHeat.Enabled = false;
t.Start();
}
}
//stop heating
private void btnStop_Click(object sender, EventArgs e)
{
Heating heat = new Heating();
Form1 l = new Form1();
l.Subscribe(heat);
heat.stopHeat();
btnHeat.Enabled = true;
}
private void heatIt()
{
// heat food Implementation that calls the 'Heating' class
}
public void Subscribe(Heating m)
{
m.heatComplete += SignalHeatCompleted;
m.heatStop += SignalStop;
}
private void SignalHeatCompleted(Heating m, EventArgs e)
{
MessageBox.Show( "Done, please enjoy your food");
return;
}
private void SignalStop(Heating m, EventArgs e)
{
t.Abort();
MessageBox.Show("Heating Terminated");
return;
}
public class Heating
{
public event HeatingCompleted heatComplete; // Heating Completed Event
public event HeatingStop heatStop; // Heating Stop Event
public EventArgs e = null;
public delegate void HeatingCompleted(Heating h, EventArgs e);
public delegate void HeatingStop(Heating s, EventArgs e);
public void startHeat(int temp, int min, int sec)
{
int totalSec;
totalSec = ((min*60) + sec) * 1000;
Thread.Sleep(totalSec);
if (heatComplete != null)
{
heatComplete(this, e);
}
else
{
//Use default signal if there's no subscription to this event
MessageBox.Show("*TING*");
}
return;
}
public void stopHeat()
{
if (heatStop != null)
{
heatStop(this, e);
}
}
}
}
You are creating a new instance of Form1 in your stop click event and so you are talking to a completely different t from the one in your start click.
You also probably want to have a single instance of Heat that you assign in heatIt and then use that reference in your stop click.
Also for background processing you probably want to look at the BackgroundWorker class to do the heavy lifting for you.
Several remarks:
You should never use Thread.Abort to stop background tasks. This is a bad practice, as it forces aborting the background thread regardless of its state. Use a volatile bool flag instead, and check (every once in a while) if its value has changed.
It seems that your Form represents a UI for business logic extracted into a separate class (Heating). In that case, it probably makes sense to have only a single instance per form, and put it in a private field. Right now you are creating a new instance inside your Stop method, which is probably wrong (since I presume you already use it in the heatIt method).
For each Subscribe method, try to keep a habit of adding a Unsubscribe method, which detaches event handlers at some point. This way GC can collect your listeners after they are no longer needed, and you prevent adding the same event handlers several times.
I would expect something like:
private Heating _heating;
private Thread _workerThread;
private volatile bool _stopRequest = false;
void Start_Btn_Pressed(object sender, EventArgs e)
{
// create the private instance
_heating = new Heating();
Subscribe(_heating);
// start the thread
_stopRequest = false;
_workerThread = new Thread(HeatIt);
_workerThread.Start();
}
void Stop_Btn_Pressed(object sender, EventArgs e)
{
// request stop
_stopRequest = true;
// wait until thread is finished
_workerThread.Join();
// unsubscribe
// ** note that a better place for unsubscribing
// might be at the end of the HeatIt method
Unsubscribe(_heating);
}
And, in your background worker method, you will need to have a loop which checks if _stopRequest has been set:
void HeatIt()
{
while (!_stopRequest && !finishedWork)
{
// do work
}
}
Note that you must have a place in your worker method which will check the _stopRequest flag. Otherwise the only way to stop it is to Abort it (like you did), which is not recommended.
Apart from that, you don't need to stop the thread (like you did in your SignalStop method) once the process is finished. When HeatIt method returns (ends), the thread will also end, and there is no need to do this.
Related
I need to stop a Thread when my timer is done.
But this all from a other function.
My Timer starts after Pressing Key: L. a Messagebox appears "Timer started" and my Thread starts too.
after 10 seconds, Timer stops with message but my Thread is still running.
What can i do? :/
void StartFunction()
{
Thread AB = new Thread(SEARCHING) { IsBackground = true };
AB.Start();
}
void StopFunction()
{
Thread AB = new Thread(SEARCHING);
AB.Abort();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.L)
{
StartFunction();
timer1.Start();
MessageBox.Show("Timer 1 started!");
}
}
int time = 0;
private void timer1_Tick(object sender, EventArgs e)
{
time++;
if (time == 10 && timer1.Enabled)
{
StopFunction();
MessageBox.Show("Timer 1 stoped!");
timer1.Stop();
time = 0;
}
}
Idle_Mind is correct on how to accomplish this. Below is a working example using .NET 6.
One important detail is to use Thread.Join(). This will tell your caller to block until the loop is exited and the method returns.
Here I use the command console to key off the switching of the _running flag. You can do the same with a timer or whatever else. Keep in mind that you should probably also implement IDisposable in your class with the thread in it and set _running to false and do the join there as well. That way, you can instantiate the object with using.
namespace Lala
{
class AB : IDisposable
{
private bool _running = false;
private readonly Thread _thread;
public AB() => _thread = new Thread(Method);
private void Method()
{
while (_running)
{
Console.WriteLine("doing stuff");
Thread.Sleep(1000);
}
}
public void StartMethod()
{
_running = true;
_thread.Start();
}
public void StopMethod()
{
_running = false;
_thread.Join();
}
public void Dispose() => StopMethod();
}
public class Program
{
public static void Main()
{
Console.WriteLine("Launching a Thread. Press any key to stop it");
using AB ab = new();
// AB ab = new(); // if using is not appropriate
ab.StartMethod();
while (!Console.KeyAvailable)
Thread.Sleep(10);
// ab.StopMethod();// if using is not appropriate
}
}
}
Using modern methods you would write something like
private async void Form1_KeyDown(object sender, KeyEventArgs e)
{
var cts = new CancellationTokenSource(10000);
var task = Task.Run(() => Search(cts.Token));
try
{
var result = await task;
// handle result
}
catch (OperationCanceledException)
{
// handle cancelled
}
catch (Exception)
{
// handle other exceptions
}
}
public int Search(CancellationToken cancel)
{
while (true)
{
cancel.ThrowIfCancellationRequested();
// Do searching
if (found)
return result;
}
}
This would use thread pool threads instead of dedicated threads, and avoids the need to manually managing a timer. It also makes it easy to handle the result from the operation, if there are any.
Unfortunately, everything posted before didn't work for me or i just had not understand what i have to do.
Iam a C# Novice and I have a hard time understanding technical terms.
But i found a solution to make this possible.
This stops not the Thread but it Stops the while there has a function in a Thread.
First set a bool on top under public partial class:
public partial class Form1 : Form
{
private volatile bool m_StopThread;
then you have to give your while in the function this:
while (!m_StopThread)
this means that your while is still not running until this is set true.
After this is set, you give your Button or Timer a function maybe like this:
if ()
{
m_StopThread = true;
}
If this function is active your Thread will Start, because now its true and not longer false.
at the same way you can stop this again by set this function to false again.
If the solution I'm explaining has already been suggested, I thank you.
And hope it helps others.
But unfortunately I couldn't understand how to proceed now.
Thank you to those who go out of their way to help people like me every day. :)
Sorry for my bad English. Hope someone suggests me a better version of my question.
I've searched but seemed like I couldn't find the answer for my problem.
Currently, I'm writing a C# WPF app. This app will perform a heavy task in a long time. So I've decided to create another class with that heavy method and pass that method to another thread. I have to create a class to do that because the heavy method takes parameters.
I want the ability to suspend and resume that thread. I've known that I should use a ManualResetEvent object or Thread.Sleep method.
After many hours of trying and testing, getting confused why I always end up suspend the UI thread but the heavy thread is still running. What I've tried were:
Create a ManualResetEvent object called mre inside the HeavyClass. When user click the Pause button, the UI class will call the method heavyClass.mre.WaitOne().
class HeavyClass
{
// properties
ManualResetEvent mre = new ManualResetEvent(false);
public void HeavyRun()
{
//Do something takes really long time
//And doesn't have any loops
}
}
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
heavyClass.mre.WaitOne();
}
}
Create a method called SleepThread inside the HeavyClass. When user click the Pause button, the UI class will call the method heavyClass.SleepThread().
class HeavyClass
{
//properties
ManualResetEvent mre = new ManualResetEvent(false);
public void SleepThread()
{
Thread.Sleep(Timeout.Infinite);
//mre.WaitOne();
//They are the same behavior
}
public void HeavyRun()
{
//Do something takes really long time
//And doesn't have any loops
}
}
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
heavyClass.SleepThread();
}
}
Create an EventHandler<MainWindow> PauseThread inside the UI class, then write its handle inside the HeavyClass. When user click the Pause button, the UI class will trigger the event PauseThread(this, this).
class MainWindow : Window
{
// properties
private HeavyClass heavyClass = new HeavyClass();
public event EventHandler<MainWindow> PauseThread;
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun);
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
PauseThread(this, this);
}
}
class HeavyClass
{
// properties
ManualResetEvent mre = new ManualResetEvent(false);
public void HeavyRun()
{
MainWindow.PauseThread += (s, E) =>
{
Thread.Sleep(Timeout.Infinite);
//mre.WaitOne();
//They are the same behavior
};
//Do something takes really long time
//And doesn't have any loops
}
}
As I said above, I always paused the UI thread and the heavy task is still running.
And finally in the end, I've known the essence of my problem. That is: which thread calls Thread.Sleep() or WaitOne() will be blocked. Yeah, "which thread", not "which class".
Everything makes sense for me now. But that doesn't help me to achieve my goal. And that leads me to think that I am doing the seemingly impossible thing. It's clearly that I want to pause a thread by another thread. But that another thread is the one who calls any kinds of "suspend thread", so it is the one who is suspended. I don't have any idea about how to make the heavy method to be suspended by itself. It is running, how the hell it could know when the user click the Pause button?
I am at a total loss. Someone please help me to make my app works as expected.
By the way, this impossible thing makes me think that I am doing things wrong way, is it?
UPDATE: If you like to see my heavy task, actually it is very simple
class HeavyClass
{
public string filePath = "D:\\Desktop\\bigfile.iso";//This file is about 10GB
public string HeavyRun()
{
string MD5Hash;
MD5 md5 = MD5.Create();
Stream stream = File.OpenRead(filePath);
MD5Hash = Encoding.Default.GetString(md5.ComputeHash(stream));
return MD5Hash;
}
}
To make a thread suspendable, the work in the thread must be separable. In your case md5.ComputeHash(stream) will do all the work, and there is not way to make sure that thread will suspend at a right(saft) point inside md5.ComputeHash(stream). So you have to rewrite HeavyClass like below. Please notice that those codes are not the best approach of handling a thread, and I just try to keep it as same as the original.
class HeavyClass
{
MD5 _md5 = MD5.Create();
MethodInfo _hashCoreMI = _md5.GetType().GetMethod("HashCore", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo _HashFinalMI = _md5.GetType().GetMethod("HashFinal", BindingFlags.NonPublic | BindingFlags.Instance);
WaitHandle _signal;
public void HeavyClass(WaitHandle signal)
{
_signal = signal;
}
public string HeavyRun(string filename)
{
byte[] buffer = new byte[4096];
int bytesRead = 0;
_signal.Set();
using(FileStream fs = File.OpenRead(filename))
{
while(true)
{
bytesRead = fs.Read(buffer, 0, 4096);
if (bytesRead > 0)
{
_hashCoreMI.Invoke(_md5, new object[] { buffer, 0, bytesRead });
}
else
{
break;
}
// if WaitHandle is signalled, thread will be block,
// otherwise thread will keep running.
_signal.WaitOne();
}
}
byte[] hash = _hashFinalMI.Invoke(_md5, null);
_md5.Initialize();
return Encoding.ASCII.GetString(hash);;
}
}
class MainWindow : Window
{
private HeavyClass _heavyClass;
private ManualResetEvent _mre;
public MainWindow()
{
InitializeComponent();
_mre = new ManualResetEvent(true);
_heavyClass = new HeavyClass(_mer);
}
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(heavyClass.HeavyRun("D:\\Desktop\\bigfile.iso"));
t.Start();
}
private void buttonPause_Click(object sender, RoutedEventArgs e)
{
_mre.Reset();
}
private void buttonResume_Click(object sender, RoutedEventArgs e)
{
_mre.Set();
}
}
I made a thread at load event like below:
Thread checkAlert = null;
bool isStop = false;
private void frmMain_Load(object sender, EventArgs e)
{
checkAlert = new Thread(CheckAlert);
checkAlert.Start();
}
void CheckAlert()
{
while (!isStop)
{
Thread.Sleep(60000);
//do work here
}
}
Is there any way to resume the checkAlert thread during it's sleep period?( Thread.Sleep(60000);)
I tried using Thread.Interrupt() but it flows a ThreadInterruptedException, how should I handle this exception? or is there any way to resume the thread?
Edited:
I need to wake up the thread before the "sleep" end because when the user wants to quit the program, the program will have to wait for some time before it really quits ( checkAlert is still running) Is there any way to improve this case?
Based on your comments what it looks like is you need to re-design how CheckAlert works so it does not use Sleep's at all. What you should be doing is using a Timer instead.
System.Timers.Timer timer = null;
public FrmMain()
{
InitializeComponent();
timer = new System.Timers.Timer(60000);
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
//If you want OnTimedEvent to happen on the UI thread instead of a ThreadPool thread, uncomment the following line.
//timer.SynchronizingObject = this;
if(this.components == null)
this.components = new System.ComponentModel.Container();
//This makes it so when the form is disposed the timer will be disposed with it.
this.componets.Add(timer);
}
private void frmMain_Load(object sender, EventArgs e)
{
timer.Start();
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
//It is good practice not to do complicated logic in a event handler
// if we move the logic to its own method it is much easier to test (you are writing unit tests, right? ;) )
CheckAlert();
}
void CheckAlert()
{
//do work here
}
private void frmMain_Close(object sender, EventArgs e)
{
timer.Stop();
}
If you want the thread to exit automatically when your program quits, simply make it a background thread.
checkAlert = new Thread(CheckAlert);
checkAlert.IsBackground = true;
checkAlert.Start();
It looks to me like you're trying to create a thread which handles two types of events: do something and stop running.
Rather than using a shared variable (isStop) and some other technique to interrupt the thread in order to do work, you might want to use threading events (not to be confused high-level UI Event objects) to control your thread.
AutoResetEvent stop = new AutoResetEvent(false);
AutoResetEvent check = new AutoResetEvent(false);
private void CheckAlert() {
WaitHandle[] handles = new WaitHandle[] { stop, check };
for (;;) {
switch (AutoResetEvent.WaitAny(handles)) {
case 0:
return;
case 1:
// do work
break;
}
}
}
Calling check.Set() in your code will trigger the "do work" branch in the thread and stop.Set() will cause the thread to terminate gracefully.
Once your code has called stop.Set() to terminate the thread, it can call the thread's Join() method to wait until the thread terminates.
EDIT
I misunderstood the question. I will leave the code above in case anyone finds it useful.
If all you want to do is have a thread that performs a task once a minute and stop on demand, you can use the following code:
AutoResetEvent stop = new AutoResetEvent(false);
void CheckAlert() {
var time = new TimeSpan(0, 1, 0); // one minute
while (!stop.WaitOne(time)) {
// do work
}
}
private Thread checkThread;
private void frmMain_Load(object sender, EventArgs e) {
checkThread = new Thread(CheckAlert);
checkThread.Start();
}
private void frmMain_Close(object sender, EventArgs e) {
stop.Set(); // signal thread to stop
checkThread.Join(); // wait for thread to terminate
}
You can see an explanation on how to wake a sleeping thread here:
https://msdn.microsoft.com/en-us/library/tttdef8x%28v=vs.100%29.aspx
and this is a complete example (as you can see, Thread.Interrupt is the good choise... however you have to catch it to continue normal thread execution):
public class HVCSensor : HVCDevice, IDisposable
{
private Thread myThread;
private const int execute_timeout = ((10 + 10 + 6 + 3 + 15 + 15 + 1 + 1 + 15 + 10) * 1000);
private bool disposed = false;
private bool paused = false;
public delegate void HVCResultsHandler(HVC_RESULT res);
public event HVCResultsHandler HVCResultsArrived;
private void OnHVCResultsArrived(HVC_RESULT res)
{
if (HVCResultsArrived != null) {
HVCResultsArrived(res);
}
}
public HVCSensor() {
myThread = new Thread(new ThreadStart(this.execute));
}
private void execute(){
while (!disposed) {
if (!paused && this.IsConnected)
{
HVC_RESULT outRes;
byte status;
try
{
this.ExecuteEx(execute_timeout, activeDetections, imageAcquire, out outRes, out status);
OnHVCResultsArrived(outRes);
}
catch (Exception ex) {
}
}
else {
try
{
Thread.Sleep(1000);
}
catch (ThreadInterruptedException e)
{
}
}
}
}
public HVC_EXECUTION_IMAGE imageAcquire
{
get;
set;
}
public HVC_EXECUTION_FLAG activeDetections
{
get;
set;
}
public void startDetection() {
if(myThread.ThreadState==ThreadState.Unstarted)
myThread.Start();
}
public void pauseDetection() {
paused = true;
}
public void resumeDetection() {
paused = false;
if (myThread.ThreadState == ThreadState.WaitSleepJoin)
myThread.Interrupt();
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
disposed = true;
myThread.Interrupt();
}
}
I have a c++/cli wrapper class which grabs frames from a camera and sends them as events.
A WPF test application Starts the camera, and updates the images.
When I click Stop, it usually ends in a deadlock, on m->streamThread->Join(). I'm suspecting the problem has to do with the frame handling event in the WPF, rather than the wrapper code.
namespace WpfTestApp
{
public partial class Window1 : Window
{
private void OnFrameArrived(object sender, EventArgs e)
{
Action a = delegate
{
// this uses Imaging.CreateBitmapSourceFromMemorySection
// to copy the frame data to the image memory
m_colorImage.UpdateImage(e.Image);
};
Dispatcher.Invoke(a);
}
private void startBtn_Click(object sender, RoutedEventArgs e)
{
m_camera.FrameArrived += m_frameHandler;
m_camera.Start();
}
private void Stop()
{
m_camera.FrameArrived -= m_frameHandler;
m_camera.Stop();
}
}
}
// Camera.h
public ref class Camera
{
public:
delegate void FrameArrivedHandler(Object^ sender, DGEventArgs^ e);
event FrameArrivedHandler^ FrameArrived;
void Start();
void Stop();
private:
void StreamThreadWorker();
Thread^ m_streamThread;
bool m_isStreaming;
}
// Camera.cpp
void Camera::Start()
{
if (m_isStreaming)
return;
m_isStreaming = true;
m_streamThread = gcnew Thread(gcnew ThreadStart(this, &Camera::StreamThreadWorker));
m_streamThread->Start();
}
void Camera::Stop()
{
if (!m_isStreaming)
return;
m_isStreaming = false;
m_streamThread->Join(); // stuck here
}
void Camera::StreamThreadWorker()
{
EventArgs^ eventArgs = gcnew EventArgs();
while (m_isStreaming)
{
eventArgs->Image = Camera->GetImage();
FrameArrived(this, eventArgs);
}
}
likely what happens is: you click Stop, this gets handled in the WPF ui dispatcher thread. So the Join call is in the ui dispatcher thread. However this same thread is also responsible for drawing the frames (the invoked call to UpdateImage). As a result, the StreamThreadWorker is waiting on FrameArrived to finish, but that cannot finish because the thread is waiting for Stop to finish. There's your deadlock.
So in order to get the StreamThreadWorker to finish, it must not be blocked by Stop. An easy way to achive this is to stop the thread from within another thread:
void Camera::Stop()
{
...
gcnew Thread( gcnew ThreadStart( this, &Camera::DoStopThread ) )->Start();
}
void Camera::DoStopThread()
{
if( !m_streamThread.Join( 3000 ) )
HandleThreadDidNotStopInTimeError(); //notify listeners there's a serious problem
m_streamThread.Abort();
m_streamThread = null;
RaiseThreadStoppedEvent(); //notify listeners that the thread stopped
}
When you subscribe to an event on an object from within a form, you are essentially handing over control of your callback method to the event source. You have no idea whether that event source will choose to trigger the event on a different thread.
The problem is that when the callback is invoked, you cannot assume that you can make update controls on your form because sometimes those controls will throw an exception if the event callback was called on a thread different than the thread the form was run on.
To simplify Simon's code a bit, you could use the built in generic Action delegate. It saves peppering your code with a bunch of delegate types you don't really need. Also, in .NET 3.5 they added a params parameter to the Invoke method so you don't have to define a temporary array.
void SomethingHappened(object sender, EventArgs ea)
{
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
return;
}
textBox1.Text = "Something happened";
}
Here are the salient points:
You can't make UI control calls from a different thread than the one they were created on (the form's thread).
Delegate invocations (ie, event hooks) are triggered on the same thread as the object that is firing the event.
So, if you have a separate "engine" thread doing some work and have some UI watching for state changes which can be reflected in the UI (such as a progress bar or whatever), you have a problem. The engine fire's an object changed event which has been hooked by the Form. But the callback delegate that the Form registered with the engine gets called on the engine's thread… not on the Form's thread. And so you can't update any controls from that callback. Doh!
BeginInvoke comes to the rescue. Just use this simple coding model in all your callback methods and you can be sure that things are going to be okay:
private delegate void EventArgsDelegate(object sender, EventArgs ea);
void SomethingHappened(object sender, EventArgs ea)
{
//
// Make sure this callback is on the correct thread
//
if (this.InvokeRequired)
{
this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
return;
}
//
// Do something with the event such as update a control
//
textBox1.Text = "Something happened";
}
It's quite simple really.
Use InvokeRequired to find out if this callback happened on the correct thread.
If not, then reinvoke the callback on the correct thread with the same parameters. You can reinvoke a method by using the Invoke (blocking) or BeginInvoke (non-blocking) methods.
The next time the function is called, InvokeRequired returns false because we are now on the correct thread and everybody is happy.
This is a very compact way of addressing this problem and making your Forms safe from multi-threaded event callbacks.
I use anonymous methods a lot in this scenario:
void SomethingHappened(object sender, EventArgs ea)
{
MethodInvoker del = delegate{ textBox1.Text = "Something happened"; };
InvokeRequired ? Invoke( del ) : del();
}
I'm a bit late to this topic, but you might want to take a look at the Event-Based Asynchronous Pattern. When implemented properly, it guarantees that events are always raised from the UI thread.
Here's a brief example that only allows one concurrent invocation; supporting multiple invocations/events requires a little bit more plumbing.
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class MainForm : Form
{
private TypeWithAsync _type;
[STAThread()]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
public MainForm()
{
_type = new TypeWithAsync();
_type.DoSomethingCompleted += DoSomethingCompleted;
var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };
var btn = new Button() { Text = "Synchronous" };
btn.Click += SyncClick;
panel.Controls.Add(btn);
btn = new Button { Text = "Asynchronous" };
btn.Click += AsyncClick;
panel.Controls.Add(btn);
Controls.Add(panel);
}
private void SyncClick(object sender, EventArgs e)
{
int value = _type.DoSomething();
MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
}
private void AsyncClick(object sender, EventArgs e)
{
_type.DoSomethingAsync();
}
private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
{
MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
}
}
class TypeWithAsync
{
private AsyncOperation _operation;
// synchronous version of method
public int DoSomething()
{
Thread.Sleep(5000);
return 27;
}
// async version of method
public void DoSomethingAsync()
{
if (_operation != null)
{
throw new InvalidOperationException("An async operation is already running.");
}
_operation = AsyncOperationManager.CreateOperation(null);
ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
}
// wrapper used by async method to call sync version of method, matches WaitCallback so it
// can be queued by the thread pool
private void DoSomethingAsyncCore(object state)
{
int returnValue = DoSomething();
var e = new DoSomethingCompletedEventArgs(returnValue);
_operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
}
// wrapper used so async method can raise the event; matches SendOrPostCallback
private void RaiseDoSomethingCompleted(object args)
{
OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
}
private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
{
var handler = DoSomethingCompleted;
if (handler != null) { handler(this, e); }
}
public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
}
public class DoSomethingCompletedEventArgs : EventArgs
{
private int _value;
public DoSomethingCompletedEventArgs(int value)
: base()
{
_value = value;
}
public int Value
{
get { return _value; }
}
}
}
As the lazy programmer, I have a very lazy method of doing this.
What I do is simply this.
private void DoInvoke(MethodInvoker del) {
if (InvokeRequired) {
Invoke(del);
} else {
del();
}
}
//example of how to call it
private void tUpdateLabel(ToolStripStatusLabel lbl, String val) {
DoInvoke(delegate { lbl.Text = val; });
}
You could inline the DoInvoke inside your function or hide it within separate function to do the dirty work for you.
Just keep in mind you can pass functions directly into the DoInvoke method.
private void directPass() {
DoInvoke(this.directInvoke);
}
private void directInvoke() {
textLabel.Text = "Directly passed.";
}
In many simple cases, you can use the MethodInvoker delegate and avoid the need to create your own delegate type.