I have a method which is rebuilding the product catalog of a webshop. This is neccessary after I change some product information. After the rebuilding method I would like to start a second method to generate full text index of the webshop. I can watch the status of the first method (RebuildCatalog). If the status is "RebuildFinished" then I would like to start the second method (GenerateFullTextIndex). I would like to use Threads functionality. Does someone can create an example of how to implementate this scenario?
I would like to use Threads functionality.
It really doesn't sound like you do. Starting one method after another finishes is as simple as:
var status = RebuildCatalog();
if (status == Status.RebuildFinished)
{
GenerateFullTextIndex();
}
No threading required. If you really think you need multiple threads, you should explain why you think they'll help. At what point do you need to perform multiple tasks concurrently?
Well, if you want to use a multiple threads and oranize your calls in chain so they are executed on another thread, but in sequence, and you are using .NET Framework 4.0>, you can use a Task Parallelism, like for example using Task::ContinueWith method.
Example (preudocode from MSDN):
Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));
getData.Start();
//or...
Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
.ContinueWith((x) => Analyze(x.Result))
.ContinueWith((y) => Summarize(y.Result));
Using events would seem to be simpler than watching the status.
In your rebuild catalog code fire a "finished" event on completion:
public event EventHandler<EventArgs> RebuildFinished;
private void Rebuild(...)
{
// Rebuild the catalog
this.RebuildFinished(this, new EventArgs(...));
}
Then handle it:
this.catalog.RebuildFinished += this.RebuildFinished;
private void RebuildFinished(object sender, EventArgs e)
{
// Rebuild the index
}
Now both of these can (and probably should) be using threads to ensure that the UI of your application stays responsive:
this.catalogThread = new Thread(new ThreadStart(this.catalog.Rebuild));
As I can assume from your question your rebuilding method probably takes up considerable time and that is why you want to run in a separate thread. Therefore I would suggest implementing Event based async pattern. When your rebuilding (async) method finishes it will throw finished event with AsyncCompletedEventArgs (which you can subclass to pass your result status) and from there you will start your second method.
BackgroundWorker bw1 = new BackgroundWorker();//To rebuild catalog.
BackgroundWorker bw2 = new BackgroundWorker();//To generate text.
public Form1()
{
InitializeComponent();
bw1.DoWork += bw1_DoWork;
bw1.RunWorkerCompleted += bw1_RunWorkerCompleted;
bw2.DoWork += bw2_DoWork;
bw2.RunWorkerCompleted += bw2_RunWorkerCompleted;
bw1.RunWorkerAsync();//Start new thread. - Rebuild catalog.
}
void bw1_DoWork(object sender, DoWorkEventArgs e)
{
//Rebuild catalog.
}
void bw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bw2.RunWorkerAsync();//Generate text.
}
void bw2_DoWork(object sender, DoWorkEventArgs e)
{
//Generate text.
}
void bw2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Whatever...
}
Related
I have an application with a gui and a Rich Text Box where I output what the program is currently doing since data processing can be quite long.
I tried two approaches for that:
1 In the Backgroundworker method I can just call the following code fine:
GlobalVar.backgroundWorkerAppendText = task.Build_CSV_List();
Processchange();
Whereas I cannot use Form1.Processchange(); in the helper class due to the non static context
2 Therefore I tried to create my very first eventhandler.
The Idea was that helper.UpdateConsole() would raise an event
public event EventHandler OnConsoleUpdate;
public void Consoleupdate()
{
OnConsoleUpdate(this, EventArgs.Empty);
}
to which the Backgroundworker listens and then calls Processchange from its context
public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StandardTasks task = new StandardTasks();
Helper helper = new Helper();
helper.OnConsoleUpdate += Processchange;
task.DoSomeStuffHere()
}
public void Processchange(object sender=null, EventArgs e=null)
{
//MessageBox.Show(GlobalVar.backgroundWorkerAppendText);
GlobalVar.next = false;
backgroundWorker1.ReportProgress(1);
while (GlobalVar.next == false)
{
helper.TimeBreaker(100,"ms");
}
}
Unfortunately this was was not successful. As soon as rising the Event I get the errormessage System.NullReferenceException which -after googling- leads me to the conclusion that there is no listerner attached to the event eventhouh I attached it in the Backgroundworker Do work.
Edit: the OnConsoleUpdate() == null as shown on the screenshot below
event = null
The helper is in another class file "helpers" which might be important for a solution.
i hope you guys can help me out.
Welcome to SO!
A few things immediately jump to mind.
First, let's get the event issue out of the way. You've got the correct approach - you need an event and method to call it, but that method should check if the event is null.
Basically, do this:
public event EventHandler OnConsoleUpdate;
public void ConsoleUpdate()
{
OnConsoleUpdate?.Invoke(this, EventArgs.Empty);
}
The above makes use of ?, a null-condition operator. You can read more about it on this MSDN page.
Second thing... it's unclear what your background worker actually IS. It sounds like it's some kind of custom class you crated? The reason it's important is because .NET actually has a BackgroundWorker class used for running operations... well, in the background. It also has an OnProgressChanged event which you can hook up to which could be used to update the UI (just remember to set the WorkerReportsProgress property to true). And to use the BackgroundWorker mentioned above, you shouldn't need to create any events of your own.
Here's how you can use the standard .NET BackgroundWorker:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
void StartBackgroundTask()
{
worker.DoWork += worker_DoWork;
//if it's possible to display progress, use this
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
//what to do when the method finishes?
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//start!
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
//perform any "finalization" operations, like re-enable disabled buttons
//display the result using the data in e.Result
//this code will be running in the UI thread
}
//example of a container class to pass more data in the ReportProgress event
public class ProgressData
{
public string OperationDescription { get; set; }
public int CurrentResult { get; set; }
//feel free to add more stuff here
}
void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
//display the progress using e.ProgressPercentage or e.UserState
//this code will be running in the UI thread
//UserState can be ANYTHING:
//var data = (ProgressData)e.UserState;
}
void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//this code will NOT be running in the UI thread!
//you should NOT call the UI thread from this method
int result = 1;
//perform calculations
for (var i = 1; i <= 10; i++)
{
worker.ReportProgress(i, new ProgressData(){ OperationDescription = "CustomState passed as second, optional parameter", CurrentResult = result });
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
result *= i;
}
e.Result = result;
}
Now, the thing about the BackgroundWorker class is that it is rather old, and with current .NET versions you can use the async / await keywords to easily handle background operations and UI updates, but this probably is going outside the bounds of this question. That said, the existence of async / await doesn't invalidate the use of BackgroundWorker which is pretty simple in its usage.
There's one more worrisome thing in your code.
public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StandardTasks task = new StandardTasks(); //<- you create a task
Helper helper = new Helper(); // <- you create a helper
helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper event
task.DoSomeStuffHere(); // <- you do stuff with the task... but the task doesn't know about your helper above! Does `StandardTasks` use `Helper`? If so, how?
}
Do note that events, unless made static, aren't global. So hooking up to an event in one instance of a class won't cause another instance of that class to "fire" that event. It seems one way to fix your issues would be to make the StandardTasks class take Helper as one of the constructor parameters, so the code would look like this:
Helper helper = new Helper(); // <- you create a helper
helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper class event to actually do something
StandardTasks task = new StandardTasks(helper); //<- you create a task which will use the helper with the hooked up event above
I get data from database on a click.
I have an event handler which when triggered should show "data retrieving..." in status bar and should change to "Ready" again just before the event handler ends.
But the text updates only once, the second Ready one. How is it generally done?
private void Next_Click(object sender, RoutedEventArgs e){
this.footerText = "Waiting for dataRetreival";
someRandomTimeTakingMethod(); //Gets Data from DB.
this.footerText = "Ready";
}
Even though code executes line 2, the view updates only when the function is over, ie only the second one actually works.
You should put your data-intensive work on a background thread so the UI can update properly. This provides the best user experience.
To elaborate on FZysset's answer with some code...
private async void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
await SomeRandomTimeTakingMethodAsync();
IsEnabled = true;
footerText.Text = "Ready";
}
private async Task SomeRandomTimeTakingMethodAsync()
{
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(2, 5)));
// or await Task.Run(() => { ... });
}
The above example allows you to leverage await/async that was introduced in .NET 4.5. Notice how nicely it flows? No nonsense!
We're putting stuff onto the background thread so the UI can remain unblocked (thus it will show your updates to your status bar and allow user interaction.) Of course, you have to be careful not to update anything on the UI from your background thread.
If you are using an older version of .NET, you can just use TPL without async/await:
private void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
Task.Factory.StartNew(() =>
{
SomeRandomTimeTakingMethod();
}).ContinueWith(t =>
{
IsEnabled = true;
footerText.Text = "Ready";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void SomeRandomTimeTakingMethod()
{
Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(2, 5)));
}
Two important things to note about the latter example:
You must provide TaskScheduler.FromCurrentSynchronizationContext() to the ContinueWith call, or you will encounter exceptions because the continuation is not on the UI thread. You must get the context in a method that isn't running on a background thread.
You will want to check for exceptions on the Task object in your ContinueWith.
This example is very rudimentary though. If you were to have a bunch of background operations kicked off with click handlers, you'd want to give yourself some helper classes/services to make life easier. (And investigate MVVM, which I cannot tell if you are using.)
A colleague of mine gave a presentation on using various asynchronous patterns in C# and .NET. You can check it out here: https://github.com/mtusk/TplLunchAndLearn
That's because you're "someRandomTimeTakingMethod" is launched on the UI Thread. Therefore it will not update the view until it is finished.
To go around this you have the following possibilities :
Make your method "someRandom..." asynchronous with a task, and use the await operator : http://msdn.microsoft.com/en-us/library/hh191443.aspx
Launch your randomTimeTaking method into a thread, and launch an event when your execution is finished, to update the footer text
I strongly recommend you the first option, for some sample : http://msdn.microsoft.com/en-us/library/hh873191.aspx
You need to run those lines asynchronously. You can do that using the Task class:
private void Next_Click(object sender, RoutedEventArgs e){
Task.Factory.StartNew(() => footerText = "Waiting for dataRetreival");
someRandomTimeTakingMethod(); //Gets Data from DB.
Task.Factory.StartNew(() => footerText = "Ready");
}
There is one way to do it using Dispatcher. The original post is here.
The code is:-
private void Next_Click(object sender, RoutedEventArgs e){
UpdateUI("Please wait for data retrieval", delegate() { someRandomTimeTakingMethod(); });
this.footerText = "Ready";
}
public delegate void NoArgsDelegate();
public void UpdateUI(string description, NoArgsDelegate operation)
{
this.FooterText= description;
DispatcherFrame frame = new DispatcherFrame();
DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, operation);
dispatcherOperation.Completed += delegate(object sender, EventArgs e)
{
frame.Continue = false;
};
Dispatcher.PushFrame(frame);
}
If my understanding is right, this uses Asynchronous programming, not different thread. The thread will update UI first and then call the someRandomTimeTakingMethod().
I hate that my first question seems to have been answered many times, but I'm still having a tough time getting my head around how to call a method using BackgroundWorker.
I'm processing a very large text file using a series of classes and methods. The entire process is kicked off after the user selects a tool strip item. Sequentially, it goes like this:
User selects the tool strip item
User selects a file to be processed via a dialog box
The action starts
I think I can wrap everything into BackgroundWorker from the moment the user pops the initial dialog box, but what I'd like to do for now is just put the method where all the heavy lifting is done into its own instance of BackGroundWorker. I'll add a ProgressBar, too, but I think I can handle that if I can just get the BackgroundWorker process rolling.
From the top (pseudocode used for example purposes. Much omitted for brevity):
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
string fileName = openSingleFile.FileName;
processFile(fileName);
}
static public void processFile(string fileName)
{
// many vars/loops exist but not shown
foreach (data in bigData)
{
processItem(stringA, stringB); // <-- this method is where the expensive work is done
x++;
}
}
I've created an instance of BackgroundWorker...:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Things go here
}
...and I've tried too many things to list, so I've gone back to the beginning for the presentation above.
If I'm understanding BackgroundWorker, I'll need to do the following:
Replace processItem(stringA, stringB) in the above code with something like:
backgroundWorker1.RunWorkerAsync(processItem(stringA, stringB));
...and then do some type of DoWork call?
...and then do some type of RunWorkerCompleted call?
Not sure why my brain is freezing, but I'm embarrassed at the amount of time I've spent on this with no result. Any help would be greatly appreciated. Without StackOverflow, I would have been DOA a long time ago.
FYI: I've referenced other SO posts, MSDN, and DotNetPerls examples. I'm just missing something conceptually, I suppose.
Replace processItem(stringA, stringB) in the above code with something like...
No, that's how you got in trouble. You most definitely want to move the processFile() call to the worker. There is no perceivable benefit from running processItem() in a worker, at least not in the snippet you posted. And doing so is difficult, it would require starting more than one worker. One for each item. Having a lot of workers that each do little work is not very healthy. If it is really necessary then you don't want to use BackgroundWorker, you'll want an entirely different approach with several Threads that consume packets of work from a thread-safe queue. Don't go there if you can avoid it.
The only non-trivial problem to solve is passing the string that processFile() needs. Luckily BackgroundWorker.RunWorkerAsync() has an overload that takes a single object. Pass your string. Obtain its value in your DoWork event handler, casting e.Argument back to a string. Thus:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
string path = (string)e.Argument;
processFile(path);
}
private void processToolStripMenuItem_Click(object sender, EventArgs e) {
backgroundWorker1.RunWorkerAsync(openSingleFile.FileName);
processToolStripMenuItem.Enabled = false;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
processToolStripMenuItem.Enabled = true;
}
Starting up a new background worker is an expensive operation. You don't want to be starting one for each iteration of a loop. Instead, put the entire loop inside of a single background worker's scope.
When ToolStripMenuItem_Click is run create your background worker, have processFile be what is done in the DoWork event handler.
Make sure that when doing that work you're really just doing that work, not updating the UI. You'll want to separate business logic from the user interface. If you want to update the UI with some current progress then call ReportProgress and ensure that there is an event handler to properly update the UI.
If you need to update the UI when the work is all done then you can do so in the RunWorkerCompleted event handler. If the work you are doing generates some result that is used to update the UI use the Result property of the background worker to pass it from the DoWork method to the completed handler.
BackgroundWorker bgw;
In the Load event or constructor:
bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
//bgw.WorkerSupportsCancellation = true;
bgw.DoWork += bgw_DoWork;
bgw.ProgressChanged += bgw_ProgressChanged;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
/
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
string fileName = openSingleFile.FileName;
bgw.RunWorkerAsync(fileName);
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
string fileName = (string)e.Argument;
processFile(fileName);
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int Progress = e.ProgressPercentage;
//Update progressbar here
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Job completed
}
I'm using a timer to reset a lable I use as a warning box. Basically, if the user does something (more specifically, something goes wrong, ex : He uses a word not recognized by the program), this catches what went wrong early and returns to him what happened so he can change the input.
The reset blanks out the label after 5 seconds to prevent him from seeing something like "please do not use chinese characters" and maybe still thinking an old error is still up. This is what I got reading the invoke (since I hear begininvoke requires an endinvoke, I chose invoke).
private void lblWrn_TextChange(object sender, EventArgs e)
{
Timee = new System.Timers.Timer(5000);
Timee.Elapsed += new ElapsedEventHandler(timerClearWrn);
Timee.Enabled = true;
}
string empty = "";
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Invoke(new Action<Label>(lblWrn), new object[] { lblWrn, "" });
}
I am not too sure where I am going wrong with this, and looking up examples, cannot figure out which part to change. Can someone explain to me the error or invoke a bit more?
If it's a Windows Forms application, use System.Windows.Forms.Timer, then you don't need Invoke, as the timer callback is executed on the main thread.
Also, don't create a new timer on every text change.
Actually, Control.BeginInvoke does not need an EndInvoke; it is Delegate.BeginInvoke that does.
First, I would also recommend using a Windows.Forms.Timer, since it looks like you are using winforms - that will automatically fire on the UI thread, making all the problems go away - just run the code you want to run in the handler (don't use Invoke etc)
The problem in your example is that the parameters don't match; an Action<> expects a method name (more accurately: a method group) to be invoked, and the parameters in the array must be suitable. Since you don't show the method you plan to invoke, I can't help there - but lblWarn isn't a method (it is a field).
on this line
lblWrn.Invoke(new Action(lblWrn), new object[] { lblWrn, "" });
shouldn't the bold part be a function and not a object?
You have a few options. Option 1 is a little clunky. Options 2 and 3 are better.
Option 1: Continue with general strategy of using Control.Invoke but use code that calls Invoke correctly, disable auto resetting of the timer, and removes the event handler.
private void lblWrn_TextChange(object sender, EventArgs e)
{
var Timee = new System.Timers.Timer(5000);
Timee.Elapsed += this.timerClearWrn;
Timee.AutoReset = false; // Raise the Elapsed event only once
Timee.Enabled = true;
}
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Invoke(
(MethodInvoker)(()=>
{
lblWrn.Text = "";
}), null);
var Timee = (System.Timers.Timer)sender;
Timee.Elapsed -= this.timerClearWrn;
}
Option 2: Use a System.Windows.Forms.Timer instead of System.Timers.Timer.
Option 3: Use the SynchronizingObject property of System.Timers.Timer. This is my preferred option when timers are to be created and used dynamically from a UI thread.
private void lblWrn_TextChange(object sender, EventArgs e)
{
var Timee = new System.Timers.Timer(5000);
Timee.Elapsed += this.timerClearWrn;
Timee.AutoReset = false; // Raise the Elapsed event only once
Timee.SynchronizingObject = this; // Tell the Timer to raise the Elapsed event on the UI thread
Timee.Enabled = true;
}
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Text = "";
var Timee = (System.Timers.Timer)sender;
Timee.Elapsed -= this.timerClearWrn;
}
I have this code which seems pretty straightforward but the AutoResetEvent never gets signalled. Nothing seems to get returned from the web services and the WaitAll just times out after ten seconds. Everything works fine without the threading jiggerypokery so its not a web service issue. What am I doing wrong?
AutoResetEvent[] autoEvents;
ObservableCollection<Tx3.ResourceService.ResourceTime> resourceTime;
ObservableCollection<Tx3.ResourceService.ResourceTimeDetail> resourceTimeDetail;
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
autoEvents = new AutoResetEvent[]
{
new AutoResetEvent(false),
new AutoResetEvent(false),
};
var resourceService = getResourceServiceClient();
// Get ResourceTime data for this user
resourceService.ListResourceTimeAsync(CategoryWorkItemId, ResourceId);
resourceService.ListResourceTimeCompleted += new EventHandler<Tx3.ResourceService.ListResourceTimeCompletedEventArgs>(resourceService_ListResourceTimeCompleted);
// Get ResourceTimeDetails
resourceService.ListResourceTimeDetailAsync(CategoryWorkItemId, ResourceId);
resourceService.ListResourceTimeDetailCompleted += new EventHandler<ListResourceTimeDetailCompletedEventArgs>(resourceService_ListResourceTimeDetailCompleted);
WaitHandle.WaitAll(autoEvents, 10000);
System.Diagnostics.Debug.WriteLine("do something with both datasets");
}
void resourceService_ListResourceTimeCompleted(object sender, Tx3.ResourceService.ListResourceTimeCompletedEventArgs e)
{
resourceTime = e.Result;
autoEvents[0].Set();
}
void resourceService_ListResourceTimeDetailCompleted(object sender, ListResourceTimeDetailCompletedEventArgs e)
{
resourceTimeDetail = e.Result;
autoEvents[1].Set();
}
I can offer a naive first guess: it looks like you're adding the event handlers after calling the methods that start the asynchronous operations; it's possible there's a race condition in there or some other issue. Could you switch the order of operations so you attach the event handler, and then begin the operation?
These are AutoResetEvent objects -- looks like you want a ManualResetEvent -- the auto version triggers anything waiting, but immediately resets. Manual ones stay triggered so if the callback happens before you get to the WaitAll, it'll just fall through immediately.
Also, qid is correct -- you're attaching your event handlers too late too...so there's two different bugs going on here.
Are you using this code on a thread that is marked with the STA attribute, for example the main UI thread? If so, the WaitAll method is not supported on these threads.
Check here.