I am working on this example, but I am unable to mesh the callback and the threading.
What I want is this.
1) Press button
2) Start the progress bar running
3) Call to a new thread to perform some long running process
4) A callback on the long running process should trigger the progress bar to stop.
Below I have something...Although the callback parameter for DoSomethingInThread comes in as null.
The StopProgressBar() acts on the ProgressBar control, so it cannot be static.
static bool done;
static readonly object locker = new object();
static ParameterizedThreadStart threadStarter = new ParameterizedThreadStart(DoSomethingInThread);
private Thread workerThread = new Thread(threadStarter);
public delegate void StopProgressBarCallback()
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
StartProgressBar();
workerThread.Start();
}
static void DoSomethingInThread(object callback)
{
StopProgressBarCallback stopper = callback as StopProgressBarCallback;
lock (locker)
{
Thread.Sleep(5 * 1000);
}
stopper();
}
private void StartProgressBar()
{
progressBar1.MarqueeAnimationSpeed = 30;
progressBar1.Style = ProgressBarStyle.Marquee;
}
public void StopProgressBar()
{
progressBar1.Style = ProgressBarStyle.Continuous;
}
Have a look at the backgroundworker class, it is more suitable for what you are trying to do and a whole lot easier to get to grips with!
Generally, you shouldn't ever be 'newing' up Thread instances. It is better to use the thread pool, a background worker or if you're on .net 4, a task object from the thread parallel library.
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();
}
}
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 tried to wrap the dispatcher in a thread. But the result is not what i expect. How can i solve that problem?
public void Start()
{
ThreadStart ts = inner;
Thread wrapper = new Thread(ts);
wrapper.Start();
}
private void inner()
{
_Runner.Dispatcher.Invoke(_Runner.Action, DispatcherPriority.Normal);
}
You have not shown us enough code/explained yourself well enough to be able to provide a good answer, but I'm guessing your action (_Runner.Action) is expensive and slow to execute. If so, that is why your UI is unresponsive. You're essentially telling the Dispatcher to run that expensive operation on the UI thread when what you really want to do is run as much of your operation on the background thread as possible, and then marshal back to the UI thread via the Dispatcher only when necessary.
When you fire an action through/on the dispatcher, that action is called on the UI thread.
My guess is that you are doing the work/processing in the _Runner.Action function and it is tying up the UI thread. You'll have to do the main processing part in the inner() function and then call the Dispatcher for the final update details.
If you absolutely must process on the dispatcher, break your process into smaller pieces and call Dispatcher.BeginInvoke() for each piece so other events can be processed in between your process.
You need to break Runner.Action into two parts - the long running part that does the calculation and the part that updates the GUI.
After you do that you call the long running part in the background thread and use the dispatcher only on the UI update part.
By the way, you should also probably use BeginInvoke and not Invoke.
If the long running part of Runner.Action is updating the GUI than you can't use a background thread to solve your problem - there are solutions for slow GUI operations but they change depending on what exactly you are trying to do.
Here is an example that will let you run WPF applications with multiple UI threads. I believe this will help you. Refer to this http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
Thread lThread = new Thread(() =>
{
var lWnd = new Window1();
lWnd.Show();
lWnd.Closed += (sender2, e2) => lWnd.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
lThread.SetApartmentState(ApartmentState.STA);
lThread.Start();
Ditto what everyone here has said.
Additionally, you may want to look into using the BackgroundWorker class.
This is what I have started using for background tasks... I have not been using it long, so I don't know if there are bugs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SSA.Utility
{
public class BackgroundTaskManager : IDisposable
{
private System.Windows.Threading.Dispatcher _OwnerDispatcher;
private System.Windows.Threading.Dispatcher _WorkerDispatcher;
private System.Threading.Thread _WorkerThread;
private Boolean _WorkerBusy;
private System.Threading.EventWaitHandle _WorkerStarted = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset);
public BackgroundTaskManager()
{
_OwnerDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_WorkerThread = new System.Threading.Thread(new System.Threading.ThreadStart(WorkerStart));
_WorkerThread.Name = "BackgroundTaskManager:" + DateTime.Now.Ticks.ToString();
_WorkerThread.IsBackground = true;
_WorkerThread.Start();
_WorkerStarted.WaitOne();
}
public Boolean IsBusy
{
get { return _WorkerBusy; }
}
public System.Windows.Threading.Dispatcher Dispatcher
{
get {
return _WorkerDispatcher;
}
}
public System.Windows.Threading.Dispatcher OwnerDispatcher
{
get
{
return _OwnerDispatcher;
}
}
private void WorkerStart()
{
_WorkerDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_WorkerDispatcher.Hooks.DispatcherInactive += WorkDone;
_WorkerDispatcher.Hooks.OperationPosted += WorkAdded;
_WorkerStarted.Set();
System.Windows.Threading.Dispatcher.Run();
}
private void WorkAdded(Object sender, System.Windows.Threading.DispatcherHookEventArgs e)
{
_WorkerBusy = true;
}
private void WorkDone(Object sender, EventArgs e)
{
_WorkerBusy = false;
}
public void Dispose()
{
if (_WorkerDispatcher != null)
{
_WorkerDispatcher.InvokeShutdown();
_WorkerDispatcher = null;
}
}
}
}
// Useage (not tested)
private SSA.Utility.BackgroundTaskManager _background = new SSA.Utility.BackgroundTaskManager();
public void LongTaskAsync()
{
_background.Dispatcher.BeginInvoke(new Action(LongTask), null);
}
public void LongTask()
{
System.Threading.Thread.Sleep(10000); // simulate a long task
_background.OwnerDispatcher.BeginInvoke(new Action<STATUSCLASS>(LongTaskUpdate), statusobject);
}
public void LongTaskUpdate(STATUSCLASS statusobject) {
}
Yes. _Runner.Action is the problem. Some long-timed methods used in the Dispatcher block. But solution is "dont use the any thread not related to UI in the dispatcher"
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.");
}