I'm trying to write multithreading code and facing some synchronization questions. I know there are lots of posts here but I couldn't find anything that fits.
I have a System.Timers.Timer that elapsed every 30 seconds it goes to the db and checks if there are any new jobs. If he finds one, he executes the job on the current thread (timer open new thread for every elapsed). While the job is running I need to notify the main thread (where the timer is) about the progress.
Notes:
I don't have UI so I can't do beginInvoke (or use background thread) as I usually do in winforms.
I thought to implement ISynchronizeInvoke on my main class but that looks a little bit overkill (maybe I'm wrong here).
I have an event in my job class and the main class register to it and I invoke the event whenever I need but I'm worrying it might cause blocking.
Each job can take up to 20 minutes.
I can have up to 20 jobs running concurrently.
My question is:
What is the right way to notify my main thread about any progress in my job thread?
Thanks for any help.
You can also use lock to implement a thread-safe JobManager class that tracks progress about the different worker threads. In this example I just maintain the active worker threads count, but this can be extended to your progress reports needs.
class JobManager
{
private object synchObject = new object();
private int _ActiveJobCount;
public int ActiveJobsCount
{
get { lock (this.synchObject) { return _ActiveJobCount; } }
set { lock (this.synchObject) { _ActiveJobCount = value; } }
}
public void Start(Action job)
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (sender, e) =>
{
this.ActiveJobsCount++;
job();
this.ActiveJobsCount--;
};
timer.Start();
}
}
Example:
class Program
{
public static void Main(string[] args)
{
var manager = new JobManager();
manager.Start(() => Thread.Sleep(3500));
while (true)
{
Console.WriteLine(manager.ActiveJobsCount);
Thread.Sleep(250);
}
}
}
You can notify the main thread of progress through a callback method. That is:
// in the main thread
public void ProgressCallback(int jobNumber, int status)
{
// handle notification
}
You can pass that callback method to the worker thread when you invoke it (i.e. as a delegate), or the worker thread's code can "know" about it implicitly. Either way works.
The jobNumber and status parameters are just examples. You might want you use some other way to identify the jobs that are running, and you may want to use an enumerated type for the status. However you do it, be aware that the ProgressCallback will be called by multiple threads concurrently, so if you're updating any shared data structures or writing logging information, you'll have to protect those resources with locks or other synchronization techniques.
You can also use events for this, but keeping the main thread's event subscriptions up to date can be a potential problem. You also have the potential of a memory leak if you forget to unsubscribe the main thread from a particular worker thread's events. Although events would certainly work, I would recommend the callback for this application.
Use events. The BackgroundWorker class, for example, is designed specifically for what you have in mind.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
The ReportProgress function along with the ProgressChanged event are what you would use for progress updates.
pullJobTimer.Elapsed += (sender,e) =>
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (s,e) =>
{
// Whatever tasks you want to do
// worker.ReportProgress(percentComplete);
};
worker.ProgressChanged += mainThread.ProgressChangedEventHandler;
worker.RunWorkerAsync();
};
If you don't mind depending on .NET 3.0 you can use the Dispatcher to marshal requests between threads. It behaves in a similar way to Control.Invoke() in Windows Forms but doesn't have the Forms dependency. You'll need to add a reference to the WindowsBase assembly though (part of .NET 3.0 and newer and is basis for WPF)
If you can't depend on .NET 3.0 then I'd say you were onto the correct solution from the beginning: Implement the ISynchronizeInvoke interface in your main class and pass that to the SynchronizingObject property of the Timer. Then your timer callback will be called on the main thread, which can then spawn BackgroundWorkers that checks the DB and runs any queued jobs. The jobs would report progress through the ProgressChanged event which will marshal the call to the main thread automatically.
A quick google search revealed this example on how to actually implement the ISynchronizeInvoke interface.
Related
I have a dedicated class that includes a BackgroundWorker that is responsible for running class-specific actions from a queue - actions which require use of a COM object.
Objects the dedicated class are created during runtime from the UI thread when the application starts up (WPF). When the class' constructor is called, it instantiates a BackgroundWorker that runs asynchronously dequeuing Actions assigned from the UI thread.
However, when these Actions require data resulting from the COM object, I notice that the UI thread is waiting on the BackgroundWorker to finish the Action before reacting to user input.
How can I isolate so that the UI thread is not impacted by the COM's functions that can take up to 10 seconds to complete?
Code:
public class User(){
private BackgroundWorker Worker;
private Queue<Action> ActionQueue;
private COM COMObject; // COM is an interface exposed by the COM referenced in VS project
private bool Registered;
public User(){
this.Registered = true;
this.ActionQueue = new Queue<Action>();
this.Worker = new BackgroundWorker();
this.Worker.DoWork += new DoWorkEventHandler(DoWork);
this.Worker.DoWork += new RunWorkerCompletedEventHandler(WorkerCompleted);
this.Worker.Worker.WorkerSupportsCancellation = true;
this.Worker.Worker.RunWorkerAsync();
this.COMObject = new COM();
}
private DoWork(object sender, DoWorkEventArgs e){
// If there is something to be done (an action) in the queue
if (ActionQueue.Count > 0){
// Dequeue the action from the queue
Action queuedAction = ActionQueue.Dequeue();
// Do the action
queuedAction();
}
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e){
// While this machine continues to be registered to the app...
if (this.Registered)
{
Worker.RunWorkerAsync();
}
}
public void ConnectToDatabase(){
Action action = delegate {
COMObject.Connect(); // function can take up to 10 seconds to return
}; // end of action delegate
ActionQueue.Enqueue(action);
}
}
Use Code (in UI thread):
User user = new User();
user.ConnectToDatabase();
In my UI, during application startup, there can be up to 10 User objects created and called to connect. If I comment out the COMObject.Connect(); line in User::ConnectToDatabase and replace with Thread.Sleep(10000) the UI thread does not wait 10+ seconds. But, as is the code now, I notice that the COMObject.Connect(); line does result in 10+ seconds before any user input in the WPF app is processed again.
How can I isolate so that the functions related to the COM object do not impact the performance of the UI thread?
(Note: there is no interaction with the UI thread from the actions queued with the BackgroundWorker. Only class-specific properties are changed in those actions).
The answers always lurk in the comments :)
As #Blindy and #jdweng pointed out, the new COM() was being called on the main UI thread, whereas all the functionality of the COM object was being used on a different thread.
In addition, I did set the COM object's thread with the STAThread attribute (this.Worker.SetApartmentState(ApartmentState.STA);).
And, I did change from using a BackgroundWorker to an actual Thread.
And, last, but not least, as #Blindy called out the issue with using a Queue<Action> to do work on the Worker thread, queued from the main UI thread, I did end up using a ConcurrentQueue<Action>, per #Anders H's suggestion. I would have used Tasks, which from the amount of research I did on the topic would have solved the cross-thread access potential issue, but, because queued "work" would have to be done sequentially and relating to the COM object, the ConcurrentQueue ended up seeming like a decent solution for the time being. But, will have to revisit this later on.
I have a WinForms application on .NET 3.5. In this form, the user triggers an operation which is executed in another thread (a BackgroundWorker to be precise) so as to not block the UI thread. I'm in MVP, so all this is being done by a presenter which interacts with an interface to the view (implemented by the Windows Form). So far so good.
I would like to introduce functionality whereby a timeout period is introduced for the background operation to complete before cancelling it. Sounds simple enough. But the background operation calls a single function on a third-party component which may never return, so the cancellation capabilities of the BackgroundWorker are of no use to me here. Also, the BackgroundWorker.RunWorkerCompleted allowed me to get back on the UI thread, so I need to wait for the timeout or success and be able to get back to my calling thread (namely the UI thread).
I tried this using a plain old Thread (which does support Abort()) and a Timer running on a second thread, but can't seem to get it to work quite right since Join() is blocking my UI thread despite the description stating that it will block "while continuing to perform standard COM and SendMessage pumping". Admittedly I assumed this implied that it would continue to process Windows Messages, which was not the case.
int timeoutInMsec = 10000;
Thread connectThread = new Thread(Connect);
Thread timerThread = new Thread(() =>
{
var timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
timer.Tick += (_s, _e) =>
{
timer.Stop();
if (connectThread.ThreadState == ThreadState.Running)
connectThread.Abort();
};
};
connectThread.Start();
timerThread.Start();
timerThread.Join();
connectThread.Join();
Based on this question, I tried removing the second timer thread and adding a ManualResetEvent and calling Set() when the timer ticked, or when the Connect method did indeed complete. Here, instead of Join I used WaitOne, but unfortunately this also blocks my UI thread. I also found this other question, which a CancellationTokenSource which unfortunately is not available in .NET 3.5.
So, how can I spin my worker up and be able to terminate it after a given amount of time in .NET 3.5, while at the same time be able to get back to the thread where I spun up the worker thread to execute a sort of OnCompleted handler?
Many thanks in advance!
PS: I don't have a lot of experience in multi-threaded programming in .NET, so I'm sorry if this is trivial.
If I understood your question correctly, the following algorithm should solve your problem:
As before, create a BackgroundWorker to do your background work.
In BackgroundWorker_DoWork,
create a new thread (let's call it the "third-party thread") to call your third-party library, and then
wait for the third-party thread to finish or the timeout to elapse. (*)
That way, your UI won't block, since only the Backgroundworker thread is waiting, not the main thread.
Now about the interesting part: How do you wait for the third-party thread to finish (the step marked with (*))?
My suggestion would be to simply use "loop waiting with sleep", i.e. (pseudo-code, you can use the Stopwatch class for the timeout):
do until (third-party thread has finished or x seconds have elapsed):
Thread.Sleep for 100ms
if third-party thread has not finished:
Abort it // we don't have another choice
else
Process the result
It's not best practice, but it's simple, it gets the job done and you can always replace it with fancy cross-thread-syncronization stuff (which is non-trivial to get right) once you got it all working.
It's useless to create a Forms.Timer on a non-gui thread. Don't create it on a separate thread. Why are you Joining the threads? The usage of Join is to block the current thread until the other thread is finished.
This is untested pseudo code, this is for example purpose.
public class Form1: Form1
{
private int timeoutInMsec = 10000;
private System.Windows.Forms.Timer _timer;
private Thread _connectThread;
public Form1()
{
_connectThread = new Thread(Connect);
_connectThread.Start();
_timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
_timer.Tick += (_s, _e) =>
{
_timer.Stop();
if (_connectThread.ThreadState == ThreadState.Running)
_connectThread.Abort();
};
};
}
private void Connected()
{
}
private void Aborted()
{
}
private void Connect()
{
try
{
DoConnect3rdPartyStuff();
this.Invoke(Connected);
}
catch(ThreadAbortException)
{
// aborted
this.Invoke(Aborted);
}
}
}
Long story short, I need a precise timer in .Net - with prescision in milliseconds - meaning, if I tell it to fire an event when 10ms passes, it must do so, +-1ms. The built-in .Net Timer class has a precision of +-16ms it seems, which is unacceptable for my application.
I found this article http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer which provides a code for a timer that is exactly what I need (even more - that has precision in microseconds).
However, problem is, the OnTimer equivalent seems to be executed in another thread. So, if I add some code that does, say:
label1.Text = "Hello World";
I will get an exception, and thus I will have to actually write it like this:
Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));
This is, from what I understand, because the OnTimer event is fired from the timer's thread - where time is passed until enough has passed to be over the Interval, and then next OnTimer event is fired. The .Net Timer does not have such a problem - in OnTimer of the .Net Timer, I can freely modify controls's members.
Question: What should I change so that my timer will run it's OnTimer event in the main thread? Is adding "Invoke" the only choice?
While there are several ways of going about it, the one that I would generally prefer is to have the timer capture the value of SynchronizationContext.Current when it is created. That value will, when in the UI thread, contain the current synchronization context which can be used to execute methods in the message loop when in a UI context. This will work for winforms, WPF, silverlight, etc. All of those paradigms set a synchronization context.
Just grab that value when the timer is created, assuming it's created in the UI thread. If you want have an optional constructor/property to set the value so that you can use it even if the timer isn't created in the UI thread you can, although that shouldn't be needed most of the time.
Then just use the Send method of that context to fire the event:
public class Timer
{
private SynchronizationContext syncContext;
public Timer()
{
syncContext = SynchronizationContext.Current;
}
public event EventHandler Tick;
private void OnTick()
{
syncContext.Send(state =>
{
if (Tick != null)
Tick(this, EventArgs.Empty);
}, null);
}
//TODO other stuff to actually fire the tick event
}
There's no way around dispatching UI Element access to the main thread. If updating a UI element is really the only thing that you intend to do in the timer callback then forget about about your timer precision requirement. The user won't see the difference between 16ms and 50ms.
Otherwise carry out the time critical work in your timer callback and dispatch the rest of the UI work to the main thread:
void OnTimer()
{
// time critical stuff here
Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));
}
In wpf you can use the dispatcher class to dispatch messages in the UI thread:
Dispatcher.CurrentDispatcher.BeginInvoke(
new Action(()=> label1.Text = "Hello World"));
In winforms you need to call the invoke method:
this.Invoke(()=> label1.Text = "Hello World");
Consider two classes; Producer and Consumer (the same as classical pattern, each with their own threads). Is it possible for Producer to have an Event which Consumer can register to and when the producer triggers the event, the consumer's event handler is run in its own thread? Here are my assumptions:
Consumer does not know if the Producer's event is triggered
within his own thread or another.
Neither Producer nor Consumer are descendants of Control so they don't have
BeginInvoke method inherited.
PS. I'm not trying to implement Producer - Consumer pattern. These are two simple classes which I'm trying to refactor the producer so it incorporates threads.
[UPDATE]
To further expand my problem, I'm trying to wrap a hardware driver to be worked with in the simplest way possible. For instance my wrapper will have a StateChanged event which the main application will register to so it will be notified when hardware is disconnected. As the actual driver has no means other than polling to check its presence , I will need to start a thread to check it periodically. Once it is not available anymore I will trigger the event which needs to be executed in the same thread as it was added. I know this is a classical Producer-Consumer pattern but since I'm trying to simplify using my driver-wrapper, I don't want the user code to implement consumer.
[UPDATE]
Due to some comments suggesting that there's no solution to this problem, I would like to add few lines which might change their minds. Considering the BeginInvoke can do what I want, so it shouldn't be impossible (at least in theory). Implementing my own BeginInvoke and calling it within the Producer is one way to look at it. It's just that I don't know how BeginInvoke does it!
You want to do inter thread communication. Yes it is possible.
Use System.Windows.Threading.Dispatcher
http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.aspx
The Dispatcher maintains a prioritized queue of work items for a specific thread.
When a Dispatcher is created on a thread, it becomes the only Dispatcher that can be associated with the thread, even if the Dispatcher is shut down.
If you attempt to get the CurrentDispatcher for the current thread and a Dispatcher is not associated with the thread, a Dispatcher will be created. A Dispatcher is also created when you create a DispatcherObject. If you create a Dispatcher on a background thread, be sure to shut down the dispatcher before exiting the thread.
Yes there is a way to do this. It relies on using the SynchronizationContext class (docs). The sync context abstracts the operations of sending messages from one thread to another via the methods Send (synchronous for the calling thread) and Post(async for the calling thread).
Let's take a slightly simpler situation where you only want the capture one sync context, the context of the "creator" thread. You would do something like this:
using System.Threading;
class HardwareEvents
{
private SynchronizationContext context;
private Timer timer;
public HardwareEvents()
{
context = SynchronizationContext.Current ?? new SynchronizationContext();
timer = new Timer(TimerMethod, null, 0, 1000); // start immediately, 1 sec interval.
}
private void TimerMethod(object state)
{
bool hardwareStateChanged = GetHardwareState();
if (hardwareStateChanged)
context.Post(s => StateChanged(this, EventArgs.Empty), null);
}
public event EventHandler StateChanged;
private bool GetHardwareState()
{
// do something to get the state here.
return true;
}
}
Now, the creating thread's sync context will be used when events are invoked. If the creating thread was a UI thread it will have a sync context supplied by the framework. If there is no sync context, then the default implementation is used, which invokes on the thread pool. SynchronizationContext is a class that you can subclass if you want to provide a custom way to send a message from the producer to the consumer thread. Just override Post and Send to send said message.
If you wanted every event subscriber to get called back on their own thread, you would have to capture the sync context in the add method. You then hold on to pairs of sync contexts and delegates. Then when raising the event, you would loop through the sync context / delegate pairs and Post each one in turn.
There are several other ways you could improve this. For example, you may want to suspend polling the hardware if there no subscribers to the event. Or you might want to back off your polling frequency if the hardware does not respond.
First, please note that in .NET / the Base Class Library, it is usually the event subscriber's obligation to ensure that its callback code is executing on the correct thread. That makes it easy for the event producer: it may just trigger its event without having to care about any thread affinities of its various subscribers.
Here's a complete example step-by-step of a possible implementation.
Let's start with something simple: The Producer class and its event, Event. My example won't include how and when this event gets triggered:
class Producer
{
public event EventHandler Event; // raised e.g. with `Event(this, EventArgs.Empty);`
}
Next, we want to be able to subscribe our Consumer instances to this event and be called back on a specific thread (I'll call this kind of thread a "worker thread"):
class Consumer
{
public void SubscribeToEventOf(Producer producer, WorkerThread targetWorkerThread) {…}
}
How do we implement this?
First, we need the means to "send" code to a specific worker thread. Since there is no way to force a thread to execute a particular method whenever you want it to, you must arrange for a worker thread to explicitly wait for work items. One way to do this is via a work item queue. Here's a possible implementation for WorkerThread:
sealed class WorkerThread
{
public WorkerThread()
{
this.workItems = new Queue<Action>();
this.workItemAvailable = new AutoResetEvent(initialState: false);
new Thread(ProcessWorkItems) { IsBackground = true }.Start();
}
readonly Queue<Action> workItems;
readonly AutoResetEvent workItemAvailable;
public void QueueWorkItem(Action workItem)
{
lock (workItems) // this is not extensively tested btw.
{
workItems.Enqueue(workItem);
}
workItemAvailable.Set();
}
void ProcessWorkItems()
{
for (;;)
{
workItemAvailable.WaitOne();
Action workItem;
lock (workItems) // dito, not extensively tested.
{
workItem = workItems.Dequeue();
if (workItems.Count > 0) workItemAvailable.Set();
}
workItem.Invoke();
}
}
}
This class basically starts a thread, and puts it in an infinite loop that falls asleep (WaitOne) until an item arrives in its queue (workItems). Once that happens, the item — an Action — is dequeued and invoked. Then the thread goes to sleep again (WaitOne)) until another item is available in the queue.
Actions are put in the queue via the QueueWorkItem method. So essentially we can now send code to be executed to a specific WorkerThread instance by calling that method. We're now ready to implement Customer.SubscribeToEventOf:
class Consumer
{
public void SubscribeToEventOf(Producer producer, WorkerThread targetWorkerThread)
{
producer.Event += delegate(object sender, EventArgs e)
{
targetWorkerThread.QueueWorkItem(() => OnEvent(sender, e));
};
}
protected virtual void OnEvent(object sender, EventArgs e)
{
// this code is executed on the worker thread(s) passed to `Subscribe…`.
}
}
Voilà!
P.S. (not discussed in detail): As an add-on, you could package the method of sending code to WorkerThread using a standard .NET mechanism called a SynchronizationContext:
sealed class WorkerThreadSynchronizationContext : SynchronizationContext
{
public WorkerThreadSynchronizationContext(WorkerThread workerThread)
{
this.workerThread = workerThread;
}
private readonly WorkerThread workerThread;
public override void Post(SendOrPostCallback d, object state)
{
workerThread.QueueWorkItem(() => d(state));
}
// other overrides for `Send` etc. omitted
}
And at the beginning of WorkerThread.ProcessWorkItems, you'd set the synchronization context for that particular thread as follows:
SynchronizationContext.SetSynchronizationContext(
new WorkerThreadSynchronizationContext(this));
I posted earlier that I've been there, and that there is no nice solution.
However, I just stumbled upon something I have done in another context before: you could instantiate a timer (that is, Windows.Forms.Timer) when you create your wrapper object. This timer will post all Tick events to the ui thread.
Now if you're device polling logic is non-blocking and fast, you could implement it directly inside the timer Tick event, and raise your custom event there.
Otherwise, you could continue to do the polling logic inside a thread, and instead of firing the event inside the thread, you just flip some boolean variable which gets read by the timer every 10 ms, who then fires the event.
Note that this solution still requires that the object is created from the GUI thread, but at least the user of the object will not have to worry about Invoke.
It is possible. One typical approach is to use the BlockingCollection class. This data structure works like a normal queue except that the dequeue operation blocks the calling thread if the queue is empty. The produce will queue items by calling Add and the consumer will dequeue them by calling Take. The consumer typically runs it's own dedicated thread spinning an infinite loop waiting for items to appear in the queue. This is, more or less, how the message loop on the UI thread operates and is the basis for getting the Invoke and BeginInvoke operations to accomplish the marshaling behavior.
public class Consumer
{
private BlockingCollection<Action> queue = new BlockingCollection<Action>();
public Consumer()
{
var thread = new Thread(
() =>
{
while (true)
{
Action method = queue.Take();
method();
}
});
thread.Start();
}
public void BeginInvoke(Action method)
{
queue.Add(item);
}
}
I have a pet project that I'm working on that has multiple worker threads. Outputting everything to the console is getting hard to follow, so I want to develop a UI that will have one output area per thread. I want to know the best way for the threads to send updates to the UI. I have two ideas:
1) Have each thread set a "DataUpdated" flag when new data is available, and have the UI periodically check for new data.
2) Create each thread with a callback to a UI Update(...) method to be called when new data becomes available.
I am currently leaning toward (2) for two reasons: I dislike the idea of "checking" each thread, and because this is my first multithreaded application and (2) seems simpler than it probably is. I want to know:
Which option is preferable in terms of simplicity and efficiency?
Do you have any tips for implementing (2) or something like it (i.e. more event-driven)?
You can easily implement (2) by creating BackgroundWorker components and doing the work in their DoWork handlers:
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += /* your background work here */;
bw.ProgressChanged += /* your UI update method here */;
bw.RunWorkerAsync();
Each BackgroundWorker can report progress to the UI thread by calling ReportProgress: although this is primarily designed for reporting progress on a bounded process, that's not mandatory -- you can pass your own custom data as well if that's what your UI update requires. You would call ReportProgress from your DoWork handler.
The nice thing about BackgroundWorker is that it takes care of a lot of messy cross-threading details for you. It also conforms to the event-driven model of updates which you (rightly) prefer to explicit callbacks.
In most cases the easiest thing to do would be to use the BackgroundWorker component as suggested in itowlson's answer, and I would strongly suggest using that approach if possible. If, for some reason, you can't use a BackgroundWorker component for your purpose, such as if you're developing with .Net 1.1 (yikes!) or with compact framework, then you might need to use an alternative approach:
With Winform controls you have to avoid modifying controls on any thread other than the thread that originally created the control. The BackgroundWorker component handles this for you, but if you aren't using that, then you can and should use the InvokeRequired property and Invoke method found on the System.Windows.Forms.Control class. Below is an example that uses this property and method:
public partial class MultithreadingForm : Form
{
public MultithreadingForm()
{
InitializeComponent();
}
// a simple button event handler that starts a worker thread
private void btnDoWork_Click(object sender, EventArgs e)
{
Thread t = new Thread(WorkerMethod);
t.Start();
}
private void ReportProgress(string message)
{
// check whether or not the current thread is the main UI thread
// if not, InvokeRequired will be true
if (this.InvokeRequired)
{
// create a delegate pointing back to this same function
// the Invoke method will cause the delegate to be invoked on the main UI thread
this.Invoke(new Action<string>(ReportProgress), message);
}
else
{
// txtOutput is a UI control, therefore it must be updated by the main UI thread
if (string.IsNullOrEmpty(this.txtOutput.Text))
this.txtOutput.Text = message;
else
this.txtOutput.Text += "\r\n" + message;
}
}
// a generic method that does work and reports progress
private void WorkerMethod()
{
// step 1
// ...
ReportProgress("Step 1 completed");
// step 2
// ...
ReportProgress("Step 2 completed");
// step 3
// ...
ReportProgress("Step 3 completed");
}
}
I vote for #2 as well but with BackgroundWorkers instead of System.Threading.Threads.
You can have your worker threads raise events and have the main UI thread add event handlers. You need to be careful you're not raising too many events as it could get ugly if your worker threads are raising multiple events per second.
This article gives a quick overview.
The preferred way to implement multithreading in your application is to use the BackgroundWorker component. The BackgroundWorker component uses an event-driven model for multithreading. The worker thread runs your DoWork event handler, and the thread that creates your controls runs your ProgressChanged and RunWorkerCompleted event handlers.
When you update your UI controls in the ProgressChanged eventhandler, they are automatically updated on main thread which will prevent you from getting crossthread exceptions.
Look here for an example on how to use the backgroundworker.
If you're creating your own threads (non BackgroundWorker or ThreadPool threads) you can pass a callback method from your main thread that's called from the worker thread. This also lets you pass arguments to the callback and even return a value (such as a go/no-go flag). In your callback you update the UI through the target control's Dispatcher:
public void UpdateUI(object arg)
{
controlToUpdate.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal
, new System.Windows.Threading.DispatcherOperationCallback(delegate
{
controToUpdate.property = arg;
return null;
}), null);
}
}