I have a form that starts a thread. Now I want the form to auto-close when this thread terminates.
The only solution I found so far is adding a timer to the form and check if thread is alive on every tick. But I want to know if there is a better way to do that?
Currently my code looks more less like this
partial class SyncForm : Form {
Thread tr;
public SyncForm()
{
InitializeComponent();
}
void SyncForm_Load(object sender, EventArgs e)
{
thread = new Thread(new ThreadStart(Synchronize));
thread.IsBackground = true;
thread.Start();
threadTimer.Start();
}
void threadTimer_Tick(object sender, EventArgs e)
{
if (!thread.IsAlive)
{
Close();
}
}
void Synchronize()
{
// code here
}
}
The BackgroundWorker class exists for this sort of thread management to save you having to roll your own; it offers a RunWorkerCompleted event which you can just listen for.
Edit to make it call a helper method so it's cleaner.
thread = new Thread(() => { Synchronize(); OnWorkComplete(); });
...
private void OnWorkComplete()
{
Close();
}
If you have a look at a BackgroundWorker, there is a RunWorkerCompleted event that is called when the worker completes.
For more info on BackgroundWorkers Click Here
Or
You could add a call to a complete function from the Thread once it has finished, and invoke it.
void Synchronize()
{
//DoWork();
//FinishedWork();
}
void FinishedWork()
{
if (InvokeRequired == true)
{
//Invoke
}
else
{
//Close
}
}
Have a look at delegates, IAsyncResult, BeginInvoke and AsyncCallback
At the end of your thread method, you can call Close() using the Invoke() method (because most WinForms methods should be called from the UI thread):
public void Synchronize()
{
Invoke(new MethodInvoker(Close));
}
Solution for arbitrary thread (e.g. started by some other code), using UnmanagedThreadUtils package:
// Use static field to make sure that delegate is alive.
private static readonly UnmanagedThread.ThreadExitCallback ThreadExitCallbackDelegate = OnThreadExit;
public static void Main()
{
var threadExitCallbackDelegatePtr = Marshal.GetFunctionPointerForDelegate(ThreadExitCallbackDelegate);
var callbackId = UnmanagedThread.SetThreadExitCallback(threadExitCallbackDelegatePtr);
for (var i = 1; i <= ThreadCount; i++)
{
var threadLocalVal = i;
var thread = new Thread(_ =>
{
Console.WriteLine($"Managed thread #{threadLocalVal} started.");
UnmanagedThread.EnableCurrentThreadExitEvent(callbackId, new IntPtr(threadLocalVal));
});
thread.Start();
}
UnmanagedThread.RemoveThreadExitCallback(callbackId);
}
private static void OnThreadExit(IntPtr data)
{
Console.WriteLine($"Unmanaged thread #{data.ToInt64()} is exiting.");
}
Related
I am using VSTO, and I would like to have a progress bar for a task that operates on the Excel model (getting and setting rages) via COM Interop. When doing any task that operates on the Excel model, it is very very highly recommended to only do so from the main thread (there are many posts that discuss this).
My problem is that I would like to have a progress bar (that exists on a secondary thread) and I would like to be able to start my task (on the main thread) when the progress bar loads. Is there some way to queue a function to execute on the main thread from a secondary thread? If not, is there some other way I can set this up?
My source is below:
abstract class BaseProgressTask
{
private ProgressForm _form;
public volatile bool CancelPending;
private void ShowProgressForm()
{
_form = new ProgressForm(this) { StartPosition = FormStartPosition.CenterScreen };
_form.ShowDialog();
}
public BaseProgressTask()
{
ThreadStart startDelegate = ShowProgressForm;
Thread thread = new Thread(startDelegate) { Priority = ThreadPriority.Highest };
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
public abstract void Run();
protected void ReportProgress(int percent)
{
_form.BeginInvoke(new Action(() => _form.SetProgress(percent)));
}
protected void CloseForm()
{
_form.BeginInvoke(new Action(() => _form.Close()));
}
}
public partial class ProgressForm : Form
{
private BaseProgressTask _task;
public ProgressForm(BaseProgressTask task)
{
InitializeComponent();
_task = task;
}
private void btnCancel_Click(object sender, EventArgs e)
{
_task.CancelPending = true;
lblStatus.Text = "Cancelling...";
}
public void SetProgress(int percent)
{
myProgressBar.Value = percent;
}
private void ProgressForm_Load(object sender, EventArgs e)
{
//Any way to do this?
ExecuteOnMainThread(_task.Run);
}
}
You may consider using the BackgroundWorker component instead. It executes an operation on a separate thread and allows to report a progress in a more convinient way using event handlers. See How to: Use a Background Worker and Walkthrough: Multithreading with the BackgroundWorker Component (C# and Visual Basic) for more information.
The SendMessage function from Windows API can be used to run an action on the main thread.
In case if your main thread is a form, you can handle it with this short code:
if (InvokeRequired)
{
this.Invoke(new Action(() => MyFunction()));
return;
}
or .NET 2.0
this.Invoke((MethodInvoker) delegate {MyFunction();});
I have created a simple form home and there is another file Mouse_Tracking.cs.
Mouse_Tracking.cs class is a thread class. I want to start and stop that thread using two different button click in home form.
How can I do this ?
Main form:
namespace computers
{
public partial class home : Form
{
public home()
{
InitializeComponent();
}
private void btn_start_Click(object sender, EventArgs e)
{
var mst = new Mouse_Tracking();
Thread thread1 = new Thread(new ThreadStart(mst.run));
thread1.Start();
}
private void btn_stop_Click(object sender, EventArgs e)
{
//Here I want to stop "thread1"
}
}
}
Computers class:
namespace computers
{
public class Mouse_Tracking
{
public void run()
{
// Some method goes here
}
}
You shouldn't kill threads from the outside. Instead, you should gently ask your thread to terminate, and in your thread you should respond to that request and return from the thread procedure.
You could use an event for that. E.g. add the following to your form class:
AutoResetEvent evtThreadShouldStop = new AutoResetEvent();
In your run method, check if the svtThreadShouldStop event is set every 0.1-1 seconds, if it’s set, return from the thread function, e.g. if( evtThreadShouldStop.WaitOne( 0 ) ) return;
And in your btn_stop_Click call evtThreadShouldStop.Set();
P.S. It’s rarely a good decision to create your own thread: creating and destroying threads is expensive. The runtime already has the thread pool you can use for your own background processing. To post your background task to a pool thread instead use e.g. ThreadPool.QueueUserWorkItem method. You can use same technique with AutoResetEvent to request task termination.
P.P.S. The name of the Mouse_Tracking class suggest you're trying to interact with mouse from the background thread? You can't do that: you can only interact with the GUI including mouse and keyboard from the GUI thread.
Here is an example of what Soonts has suggested. It's quite old-style solution but it's simple and will work fine. But there is a number of other approaches. You can use BackgroundWorker or TPL (Task class), each of which have own thread stop mechanisms.
And I believe that it's ok to create own thread without using existing thread pool if you don't need to do it too often.
public class Mouse_Tracking
{
private ManualResetEvent _stopEvent = new ManualResetEvent(false);
public void stop()
{
_stopEvent.Set();
}
public void run()
{
while (true)
{
if (_stopEvent.WaitOne(0))
{
//Console.WriteLine("stop");
// handle stop
return;
}
//Console.WriteLine("action!");
// some actions
Thread.Sleep(1000);
}
}
}
Sometimes its quite difficult to maintain the thread. You can achieve it by using BackgroundWorker class. You will get complete demonstration on how to use it is here Stop Watch Using Background Worker. I hope it will be useful.
You could use a class like this for controlling your thread(s):
class ThreadController {
private Thread _thread;
public void Start(ThreadStart start) {
if (_thread == null || !_thread.IsAlive) {
_thread = new Thread(start);
_thread.Start();
}
}
public void Stop() {
if (_thread != null && _thread.IsAlive) {
_thread.Interrupt(); // Use _thread.Abort() instead, if your thread does not wait for events.
_thread = null;
}
}
}
Then use:
public partial class home : Form
{
public home()
{
InitializeComponent();
_thread = new ThreadController();
}
private readonly ThreadController _thread;
private void btn_start_Click(object sender, EventArgs e)
{
var mst = new Mouse_Tracking();
_thread.Start(mst.run);
}
private void btn_stop_Click(object sender, EventArgs e)
{
_thread.Stop();
}
}
I want to asynchronously update UI status when doing a long-time task . The program is a console application , however , when I execute the async operations , the UI thread will exit soon after the task begins .
How should I let the UI thread wait when my long-time task finish ?
I simplify my code as below :
public static class Program
{
static void Main()
{
WorkerWrapper wp = new WorkerWrapper();
wp.ProcessData();
}
}
public class WorkerWrapper
{
private RateBar bar;
public void ProcessData()
{
bar = new RateBar();
bar.Show();
Worker wk = new Worker();
wk.WorkProcess += wk_WorkProcess;
Action handler = new Action(wk.DoWork);
var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
}
private void AsyncCallback(IAsyncResult ar)
{
Action handler = ar.AsyncState as Action;
handler.EndInvoke(ar);
}
private void wk_WorkProcess(object sender, PrecentArgs e)
{
if (e.Precent < 100)
{
bar.Precent = e.Precent;
}
}
}
public class Worker
{
public event EventHandler<PrecentArgs> WorkProcess;
public void DoWork()
{
for (int i = 0; i < 100; i++)
{
WorkProcess(this, new PrecentArgs(i));
Thread.Sleep(100);
}
}
}
public class PrecentArgs : EventArgs
{
public int Precent { get; set; }
public PrecentArgs(int precent)
{
Precent = precent;
}
}
public partial class RateBar : Form
{
public int Precent
{
set
{
System.Windows.Forms.MethodInvoker invoker = () => this.progressBar1.Value = value;
if (this.progressBar1.InvokeRequired)
{
this.progressBar1.Invoke(invoker);
}
else
{
invoker();
}
}
}
public RateBar()
{
InitializeComponent();
}
}
However , in method ProcessData() , if I add result.AsyncWaitHandle.WaitOne() in the end to wait my operation to complete , the Form will freeze .
Is there anything wrong with my way to wait the thread to complete ?
Reason that your application exiting before your "background threads" completed is when there are multiple threads application exists soon after there are not any foreground threads. This is explained more in here http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.110).aspx
You should add proper waiting mechanisms to your background threads to be completed. There are multiple ways of letting other threads know that the thread is complete. Please refer here. How to wait for thread to finish with .NET?
You shouldn't block the UI thread waiting for the result, but rather retrieve the result from EndInvoke. Your deadlock probably occurs because you are using both result.AsyncWaitHandle.WaitOne() and EndInvoke, both will block until the result is available.
In my opinion the best option is to not call result.AsyncWaitHandle.WaitOne() and just retrieve the result in the AsyncCallback
private void AsyncCallback(IAsyncResult ar)
{
Action handler = ar.AsyncState as Action;
var result = handler.EndInvoke(ar);
}
More information here. Also if you are using .net 4.0 or higher, this sort of thing can be done much easier with async/await.
I write down this solution and hope it may helps others with same question .
The key to this problem is to use a new thread to run RateBar's ShowDialog function .
public void ProcessData()
{
new Thread(() => new RateBar().ShowDialog()).Start();
Worker wk = new Worker();
wk.WorkProcess += wk_WorkProcess;
Action handler = new Action(wk.DoWork);
var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
}
Decided to not use any timers.
What i did is simpler.
Added a backgroundworker.
Added a Shown event the Shown event fire after all the constructor have been loaded.
In the Shown event im starting the backgroundworker async.
In the backgroundworker DoWork im doing:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
cpuView();
gpuView();
Thread.Sleep(1000);
}
}
In this case it's better to use two System.Threading.Timer and execute your cpu-intensive operations in these two threads. Please note that you must access controls with BeginInvoke. You can encapsulate those accesses into properties setter or even better pull them out to a view model class.
public class MyForm : Form
{
private System.Threading.Timer gpuUpdateTimer;
private System.Threading.Timer cpuUpdateTimer;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!DesignMode)
{
gpuUpdateTimer = new System.Threading.Timer(UpdateGpuView, null, 0, 1000);
cpuUpdateTimer = new System.Threading.Timer(UpdateCpuView, null, 0, 100);
}
}
private string GpuText
{
set
{
if (InvokeRequired)
{
BeginInvoke(new Action(() => gpuLabel.Text = value), null);
}
}
}
private string TemperatureLabel
{
set
{
if (InvokeRequired)
{
BeginInvoke(new Action(() => temperatureLabel.Text = value), null);
}
}
}
private void UpdateCpuView(object state)
{
// do your stuff here
//
// do not access control directly, use BeginInvoke!
TemperatureLabel = sensor.Value.ToString() + "c" // whatever
}
private void UpdateGpuView(object state)
{
// do your stuff here
//
// do not access control directly, use BeginInvoke!
GpuText = sensor.Value.ToString() + "c"; // whatever
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (cpuTimer != null)
{
cpuTimer.Dispose();
}
if (gpuTimer != null)
{
gpuTimer.Dispose();
}
}
base.Dispose(disposing);
}
You can't just throw this code into a background worker and expect it to work. Anything that updates UI elements (labels, textboxes, ...) needs to be invoked on the main thread. You need to break out your logic to get the data and the logic to update the UI.
I would say your best bet is to do this:
In the timer Tick() method:
// Disable the timer.
// Start the background worker
In the background worker DoWork() method:
// Call your functions, taking out any code that
// updates UI elements and storing this information
// somewhere you can access it once the thread is done.
In the background worker Completed() method:
// Update the UI elements based on your results from the worker thread
// Re-enable the timer.
First make sure to get your head around multithreathing and it's problems (especially UI stuff).
Then you can use somethink like
public class Program
{
public static void Main(string[] args)
{
Timer myTimer = new Timer(TimerTick, // the callback function
new object(), // some parameter to pass
0, // the time to wait before the timer starts it's first tick
1000); // the tick intervall
}
private static void TimerTick(object state)
{
// less then .NET 4.0
Thread newThread = new Thread(CallTheBackgroundFunctions);
newThread.Start();
// .NET 4.0 or higher
Task.Factory.StartNew(CallTheBackgroundFunctions);
}
private static void CallTheBackgroundFunctions()
{
cpuView();
gpuView();
}
}
Please keep in mind (just like John Koerner told you) your cpuView() and gpuView() will not work as is.
Yes you can:
In your Timer tick event:
private void timer_Tick(object sender, EventArgs e)
{
timer.Enabled = false;
backgroundworker.RunWorkerAsync();
timer.Enabled = true;
}
In your Backgroundworker dowork event:
private void backgroundworker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
//Write what you want to do
}
catch (Exception ex)
{
MessageBox.Show("Error:\n\n" + ex.Message, "System", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
I think BackgroundWorker is too complex thing for the case; with Timer it is difficult to implement guaranteed stopping.
I would like to recommend you using worker Thread with the loop which waits cancellation ManualResetEvent for the interval you need:
If the cancellation event is set then the worker exits the loop.
If there is a timeout (time interval you need exceeds) then perform system monitoring.
Here is the draft version of the code. Please note I have not tested it, but it could show you the idea.
public class HardwareMonitor
{
private readonly object _locker = new object();
private readonly TimeSpan _monitoringInterval;
private readonly Thread _thread;
private readonly ManualResetEvent _stoppingEvent = new ManualResetEvent(false);
private readonly ManualResetEvent _stoppedEvent = new ManualResetEvent(false);
public HardwareMonitor(TimeSpan monitoringInterval)
{
_monitoringInterval = monitoringInterval;
_thread = new Thread(ThreadFunc)
{
IsBackground = true
};
}
public void Start()
{
lock (_locker)
{
if (!_stoppedEvent.WaitOne(0))
throw new InvalidOperationException("Already running");
_stoppingEvent.Reset();
_stoppedEvent.Reset();
_thread.Start();
}
}
public void Stop()
{
lock (_locker)
{
_stoppingEvent.Set();
}
_stoppedEvent.WaitOne();
}
private void ThreadFunc()
{
try
{
while (true)
{
// Wait for time interval or cancellation event.
if (_stoppingEvent.WaitOne(_monitoringInterval))
break;
// Monitoring...
// NOTE: update UI elements using Invoke()/BeginInvoke() if required.
}
}
finally
{
_stoppedEvent.Set();
}
}
}
In my case I was using a BackgroundWorker ,a System.Timers.Timer and a ProgressBar in WinForm Application. What I came across is on second tick that I will repeat the BackgroundWorker's Do-Work I get a Cross-Thread Exception while trying to update ProgressBar in ProgressChanged of BackgroundWorker .Then I found a solution on SO #Rudedog2 https://stackoverflow.com/a/4072298/1218551 which says that When you initialize the Timers.Timer object for use with a Windows Form, you must set the SynchronizingObject property of the timer instance to be the form.
systemTimersTimerInstance.SynchronizingObject = this; // this = form instance.
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
I'm using following code to call Method B after N seconds method A is called. If method A
is called again within the N seconds timeout, i have to reset the time counting back to N seconds.
I cannot reference System.Windows.Form in my project, so I cannot use System.Windows.Form.Timer.
The method B must be called in the same thread A is called.
private void InitTimer()
{
timer = new BackgroundWorker();
timer.WorkerSupportsCancellation = true;
timer.WorkerReportsProgress = true;
timer.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var st = DateTime.Now;
while (DateTime.Now.Subtract(st).TotalSeconds < 10)
{
if (timer.CancellationPending)
{
e.Cancel = true;
return;
}
}
};
timer.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
MethodB();
}
else
{
timer.RunWorkerAsync();
}
};
}
public void MethodA()
{
if (timer.IsBusy)
timer.CancelAsync();
else
timer.RunWorkerAsync();
}
public void MethodB()
{
//do some stuff
}
Actually the code work, but i think it's a bit confounding. Do you know if there is a best practices to achieve the same result?
It's a shame you're stuck on .NET 2.0, because Rx extensions has a Throttle method that achieves this effect quite elegantly.
Sadly Rx requires at least .NET 3.5 SP1.
Oh well! You can always use a System.Threading.Timer to get this done instead. Synchronization can be provided by leveraging the current SynchronizationContext (this is what BackgroundWorker does).
Here's a sketch of a LaggedMethodPair class to illustrate this approach. The class takes three inputs in its constructor: an Action to be performed on-demand, another Action to serve as the callback that will be invoked when a given timeout has elapsed, and, of course, the timeout itself:
public sealed class LaggedMethodPair
{
private SynchronizationContext _context;
private Timer _timer;
private Action _primaryAction;
private Action _laggedCallback;
private int _millisecondsLag;
public LaggedMethodPair(Action primaryAction,
Action laggedCallback,
int millisecondsLag)
{
if (millisecondsLag < 0)
{
throw new ArgumentOutOfRangeException("Lag cannot be negative.");
}
// Do nothing by default.
_primaryAction = primaryAction ?? new Action(() => { });
// Do nothing by default.
_laggedCallback = laggedCallback ?? new Action(() => { });
_millisecondsLag = millisecondsLag;
_timer = new Timer(state => RunTimer());
}
public void Invoke()
{
// Technically there is a race condition here.
// It could be addressed, but in practice it will
// generally not matter as long as Invoke is always
// being called from the same SynchronizationContext.
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext()
);
}
_context = SynchronizationContext.Current;
ResetTimer();
_primaryAction();
}
void ResetTimer()
{
_timer.Change(_millisecondsLag, Timeout.Infinite);
}
void RunTimer()
{
_context.Post(state => _laggedCallback(), null);
}
}
I wrote a sample Windows Forms app to show this class in action. The form contains a LaggedMethodPair member with a timeout of 2000 ms. Its primaryAction adds an item to a list view. Its laggedCallback adds a highlighted item to the list view.
You can see that the code runs as expected.
I would encapsulate this functionality into a timer class with events that other classes can subscribe to (for example a timer.tick event).
I am trying to use AutoResetEvent, because it is capable to wait for a signal. I use it to have worker waited for the signal from A(), and if it has been too long B() will be called.
class Caller
{
AutoResetEvent ev = new AutoResetEvent(false);
public void A()
{
ev.Set();
// do your stuff
Console.Out.WriteLine("A---");
}
void B()
{
Console.Out.WriteLine("B---");
}
public void Start()
{
var checker = new BackgroundWorker();
checker.DoWork += new DoWorkEventHandler(checker_DoWork);
checker.RunWorkerAsync();
}
void checker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (!worker.CancellationPending)
{
bool called = ev.WaitOne(TimeSpan.FromSeconds(3));
if (!called) B();
}
}
}
I have tested my class roughly and it is working fine so far. Note that B will be called from worker thread, so you have to do the synchronization in B() if needed.