well, for starters, your addhandlers for the progress change and completed events should be set BEFORE invoking the thread.
Also... what does the search function do? And what do you have in your ProgressChanged and RunWorkerCompleted event handlers?
Ok thanks, I will move the addhandlers. The search function begins by calling
the main in my discover class which then creates threads to read in urls which each then crawls and parses and adds to a database. Pretty involved function from there which I am carefully stepping through now.
Code:
Private Sub Search(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim BlogDiscoverObj As New SearchBlogDiscovery.BlogDiscover
Dim reply As String = Nothing
Dim errormessage As String = Nothing
Application.DoEvents()
Try
Console.WriteLine()
Console.WriteLine()
Console.Write(Thread.CurrentThread.Name & " ...searching " & vbNewLine)
BlogDiscoverObj.Main(connectionString, userAgent, fileName, clientName, clientProj)
Dim i As Integer
For i = 1 To 100
CType(sender, BackgroundWorker).ReportProgress(i)
Thread.Sleep(100)
Next
Console.ResetColor()
Console.Write("Search Complete, Do you wish to generate queries, y or n? ")
reply = Console.ReadLine()
If reply = "y" Then
report()
ElseIf reply = "n" Then
Console.Clear()
main()
End If
Catch ex As Exception
errormessage = ex.Message
End Try
End Sub
my backgroundworker progress functions just have messages to the console to indicate what thread is doing...
Code:
Private Sub ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
Console.WriteLine("Percent Complete: " & e.ProgressPercentage)
End Sub
Private Sub RunWorkerComplete(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
Console.WriteLine("Background worker completed.")
End Sub
I have removed all user interaction from the search function ( backgroundworker process) and checked other functions too, and assume that console error messages are ok. You mentioned that the progress changed event is used incorrectly so where would I insert that? I just need to be able to track the bgw so I can trace the process for errors. Pardon me for taking up much of your time, but dearly appreciated. I will make the changes and run it but apart from what you have noted, do you think that considering the design and intent of my app, am I at least headed in the right direction?
hard to say... in this case, I wouldn't even bother with the progress changed event. create the thread, wire up the completed event, fire it off and let it do it's thing... when it's done, it fires off the completed event, and from there you do what ever cleanup you need to do... even if it's firing off a new thread (which, yes, you can do, we do that here to string together multiple BGW to pull data.)
As for tracking errors... in the event of an error, you should be aborting the thread, which will cause the completed event to fire (in theory... I've heard rumors that it's not entirely reliable for some reason)... which will then have the aborted flag set to true. the reason I said the progress change is being misused is because it's a fake loop that's done after the search is done... so the progress it's reporting is junk, and you're sleeping the thread, which slows it down, and generally the purpose for BG Threads is to let time consuming processes a chance to run with out interefereing with the main thread... the sleep negates that.
I don't know enough about what you are doing, but the way I would do it, is to create a class that holds the parameters needed to call the main funciton in your other class.... create an instance of it, fill it in, create a BGW, set the appropriate call backs, set the class as the parameter to the BGW and call the runasynch method. the run method then unpacks the parameter class, gets the data from it, creates the BlogDiscoverObj object, and calls the appropriate method. When it's done, set the object to nothing, sets up the return values, and then declares itself done.
I'm reviewing past threads and I'm not sure what gain you are getting with using the BGW either... It looks like you're using threads for the sake of using them. if you were doing some kind of looping and running several searches simultaneously, then maybe... but it looks pretty sequential to me.
I'm gonna go through this and see if I can implement it using this logic
I don't know enough about what you are doing, but the way I would do it, is to create a class that holds the parameters needed to call the main funciton in your other class.... create an instance of it, fill it in, create a BGW, set the appropriate call backs, set the class as the parameter to the BGW and call the runasynch method. the run method then unpacks the parameter class, gets the data from it, creates the BlogDiscoverObj object, and calls the appropriate method. When it's done, set the object to nothing, sets up the return values, and then declares itself done.
But the objective of my app is to provide the user the following options:
1 - load a file to perform a search and let it run, the file can have up to 1 million urls.
2 - query the database with options to select items the user needs and return items instantly to the output window or to an output file
3 - perform a single url search by entering a single url
this is why I am using threads or bgw to perform option # 1 and then implement later # 3.
I already have threads doing the url fetching and that works fine except for some synchronizing for the outputs I will figure out. But again you have been very helpful and I just wanted to make the context of the app clearer.
Right... I get all that... but since it's a console app... it's not like the user can do anything else (like they would in a windows based app) and the UI isn't getting locked up (like it would in a windows app) ... so there isn't any benefit gain from using the BGW.
Sorry tg...I am required to migrate this to a forms app after I can effectively display the concurrent execution of the two main processes i.e search and queries..
In fact I have done the forms version first in what I would call beta version and I did not even use the bgw. Just called a new form when I needed to do option 2. Why they want me to do it this way...who knows???
but my sups argument is that this app will be migrated later and he wants the performance evaluated. So...thanks for all your time and assistance...
Is it also possible if I trigger the .RunWorkerAsync() in the Timer event?
The idea is the event in the Do_Work automatically runs time to time..
Thanks. =)
What happened when you tried? If it worked then you have your answer. If it failed then you need to tell us what happened so we can diagnose the issue.
Cool. If it's feasible to do so, always try for yourself first. Developing a "look first, ask questions later" attitude is important to becoming a good developer.
not %100 sure this is the place for this - its a question about using the BackgroundWorker that is part of a class, not a form.
i have a class the processes a general tasks, ClassA. it decides, based on info passed to it, which specific routine will handle this task.
i have another class that processes specific tasks and has functions that are called to handle the task. call it ClassB.ProcessJobFunction()
this works fine, but this process can take a long time and i need to have a Form object display the current status ("processing item X of Y")
all of the examples of the BackgroundWorker use the BW as component on a form - in my case, i need it to be a member of my ClassA.
so when ClassA is told to process a job, it
1) creates a backgroundworker to process the job
2) creates a new Form object to display the current tasks progress
3) receives notifications from ClassB.ProcessJobFunction()as progress is made
- i had thought to have this function raise an event?
4) update the Form / UI
i tried using threading but it doesn't seem like the right tool. BW does seem like the right tool but i am unclear if what i want is possible with it.
any feedback would be appreciated.
Last edited by Roccoman; Aug 24th, 2010 at 04:15 PM.
using the BW IS threading... so it's either the right tool, or it isn't, but not both. The BW has a ProgressChange event that you can handle. That event would get raised on the UI thread, where you would then be able to update the display as needed. You can invoke the event when you want by calling .UpdateProgress method (I think that's right) from inside your process.
yea i worded that badly - BW is threading of course. i was refering to using either a Thread object or a BW object.
anyway i think i have found a suitable solution in the most unlikely of places: vb6 and DoEvents
i had forgotten that this was still in VB.NET (under My.Application and was reminded by a co-worker)
this allows the progress form to be updated and responsive to the user within the function ClassB.ProcessJobFunction() - and each thread has its own form so (in theory) there should be no cross-thread issues.
The BW is simply a wrapper around the threading class... just depends on how much you want to black box (the BGW will hide some extraneous plumbing while dealing with the thread directly will give you finer control)...
DoEvents.... Hmmm.... I'm not sure how I feel about that. It has its uses in VB6 mainly because VB6 didn't support threading. Which .NET does. Just because you CAN doesn't mean you SHOULD. I CAN shoot my foot with a gun... doesn't mean it's a good idea.
Each thread my have it's own form... but if I remember right, the process is still only going to have ONE UI thread... which means those extra forms may still be on the original thread. Point being, I don't think you're out of the woods.
I'm not sure where the problem is. What you're doing is extremely typical and precisely what the BGW was designed for. That's why it has a ProgressChanged even that even includes the PercentCompleted in the event args. All you need to do it set the value and call the ProgressChanged method. Then let the parent UI safely handle the UI update.
the way i wrote the ClassA object it has a private member FormWait that is the progress form for that particular job.
there is a main form for the application but it is not the one that needs to be updated during the process - ClassA shows its own FormWait at the beginning of its processing and unloads it at the end.
i suppose the question is this: if i start a new thread (via a delegate subroutine) and that routine creates a new instance of a form, is that form in the new thread or the UI thread?
btw - i have run this a number of times now and so far have no cross-thread issues with the forms (other code is raising the issue, but that is due to shared functions being called)
anyway i truly do appreciate the responses - i am a new to threading and although devouring MSDN's Help system is useful, hearing from people who really use these tools helps a ton more.
Last edited by Roccoman; Aug 24th, 2010 at 08:35 PM.
Forms aren't "in" any particular thread. Just like all data, a form is accessible on all threads. The issue is that the handle of any control including a form, has an affinity for the thread it was created on. That means that whatever thread creates the control owns the handle, which means that it's only safe to access members of that control, other than Invoke and the like, on that thread.
If you want to display a "Please Wait..." message to the user while a background operation takes place then I consider the best way to be as follows:
1. Display a form, modal or not, on the UI thread. The form should provide no interface for closing it, other than a Cancel button if the operation can be cancelled.
2. Start a secondary thread, either explicitly or using a BackgroundWorker, in that form's Load or Shown event handler.
3. When the secondary thread completes, close the form.
Forms aren't "in" any particular thread. Just like all data, a form is accessible on all threads. The issue is that the handle of any control including a form, has an affinity for the thread it was created on. That means that whatever thread creates the control owns the handle, which means that it's only safe to access members of that control, other than Invoke and the like, on that thread.
If you want to display a "Please Wait..." message to the user while a background operation takes place then I consider the best way to be as follows:
1. Display a form, modal or not, on the UI thread. The form should provide no interface for closing it, other than a Cancel button if the operation can be cancelled. 2. Start a secondary thread, either explicitly or using a BackgroundWorker, in that form's Load or Shown event handler.
3. When the secondary thread completes, close the form.
thats the problem - i need it to be the opposite. i need my function to control its own form (as there can be multiple threads running at the same time, each with their own FormWait form)
perhaps my design is wrong in this.
you solution would work for me but would require a bit of re-work.
thanks for the feedback!
Last edited by Roccoman; Aug 24th, 2010 at 08:54 PM.
thats the problem - i need it to be the opposite. i need my function to control its own form (as there can be multiple threads running at the same time, each with their own FormWait form)
I doubt that you do. What I described is most likely fine. You should be able to show multiple forms on the UI thread and then have each of those start its own secondary thread. Try the attached example.
I have the following Sub that performs a couple of loops through a datatable . I currently have a progressbar1 that works fine, however while the sub is running if the user clicks on anything the progress bar and label freeze, the sub is still running and every functions fine. SO would I be correct in assuming this is a situation for backgroundworker? and have the label and progressbar woking on there own thread??
If so I am not sure how to implement it with my sub?? I am trying to follow and learn from the example you posted. I built the sample and trying to see how my code would be implemented.
I placed my Sub "GetValue()" in the BGW do work event but thats obviously not correct because that would just a one time event
I think I need to capture the first loop of that Sub
For i As Integer = 0 To dtlist.Rows.Count - 1
Dim lat As Double = CDbl(dtlist.Rows(i)("Latitude"))
Dim lon As Double = CDbl(dtlist.Rows(i)("Longitude"))
vb Code:
Dim Coords() As Coord = GetCoords()
Dim sw As Stopwatch
sw = Stopwatch.StartNew
Me.propsprocessed.Visible = True
Me.lblCount.Visible = True
Me.ProgressBar1.Visible = True
For i As Integer = 0 To dtlist.Rows.Count - 1
Dim lat As Double = CDbl(dtlist.Rows(i)("Latitude"))
Dim lon As Double = CDbl(dtlist.Rows(i)("Longitude"))
'Labelcount
Me.lblCount.Text = CStr(i + 1)
Me.lblCount.Refresh()
'progress bar
ProgressBar1.Maximum = dtlist.Rows.Count
ProgressBar1.Minimum = 0
For d = ProgressBar1.Minimum To ProgressBar1.Maximum
@billboy: It's very simple. You perform the long-running task in the DoWork event handler. If the long-running task is an entire loop then you put the entire loop in the DoWork event handler. If you need to update the UI during the task then you call ReportProgress and handle the ProgressChanged event. If you need to update the UI after the task then you handle the RunWorkerCompleted event. That's really all there is to it.
First things first, what's this doing in your DoWork event handler?
Code:
Dim Dt As Decimal = NumericUpDown1.Value
A NumericUpDown is a control. No accessing controls in the DoWork event handler.
As for the error, that's the exception you see on the UI thread when an exception is thrown on a secondary thread. You need to catch the exception on the secondary thread, examine it and fix the issue that's causing it, just as you would do on the UI thread.
Ok I still dont understand why my exception message box doesnt appear, but I managed to view the debug out window and found the error, i needed to set my progressbar maximum state
ProgressBar1.Maximum = dtlist.Rows.Count
I know I still have some problems, with access control textbox etc... but I think you have given me enough insight to tackle rest for now....
Sorry for dragging this post back up but its the best example I found.
I'm also a bedroom hobbyist and therefore don't have any real expirence in programming so forgive me for if this is a dumb question.
I have a background worker within my form and a progress bar which works fine whilse the work being carried out is within the same form. If I move the work to a Module the work still gets carried out but my progress bar doesn't update anymore. My understanding is this is because the work and progress bar are not within the same ui thread but I dont understand how to fix this issue.
Here's a quick example of a test I setup...
My Main Form
Code:
Public Class Form_Test
Delegate Sub SetLabelText_Delegate(ByVal Label As Label, ByVal [text] As String)
Public Sub SetLabelText_ThreadSafe(ByVal Label As Label, ByVal [text] As String)
If Label.InvokeRequired Then
Dim MyDelegate As New SetLabelText_Delegate(AddressOf SetLabelText_ThreadSafe)
Me.Invoke(MyDelegate, New Object() {Label, [text]})
Else
Label.Text = [text]
End If
End Sub
Public Sub test()
Dim i As Integer = 0
Do While i < 20
i = i + 1
Me.BackgroundWorker1.ReportProgress(CInt((i / 20) * 100))
Me.SetLabelText_ThreadSafe(Label1, "Same Form - " & FormatPercent(i / 20, 0))
System.Threading.Thread.Sleep(100)
Debug.Print("Running Test 2 From Within The Same Form: " & i)
Loop
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_DoWork_1(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
test()
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker2_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker2.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Public Sub BackgroundWorker2_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker2.DoWork
test_2()
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Me.BackgroundWorker2.RunWorkerAsync(2)
End Sub
End Class
My Module Code
Code:
Module Module_Test
Public Sub test_2()
Dim i As Integer = 0
Do While i < 20
i = i + 1
Form_Test.BackgroundWorker1.ReportProgress(CInt((i / 20) * 100))
Form_Test.SetLabelText_ThreadSafe(Form_Test.Label1, "Module - " & FormatPercent(i / 20, 0))
System.Threading.Thread.Sleep(100)
Debug.Print("Running Test 2 From Within A Module: " & i)
Loop
End Sub
End Module
@jaminben, the work is carried out on a different thread to the one that owns the ProgressBar in both cases. That's the whole point. The issue is related to your use of a default instance. Follow the Blog link in my signature and check out my post on Default Form Instances to learn how they work and don't work. That should help to explain what you're doing wrong. If you still need help to fix it, post back.
Can you throw me a bone (I've read through your blog, as well as others)... And I'm not really understanding instances and how you reference between them with the background worker.
Thanks
Edit
Not too worry I worked it out... thanks for the push in the right direction though
Last edited by jaminben; May 15th, 2012 at 10:46 AM.
jm, all controls are in the UI thread. And thus, we can't access it from the BackgroundWorker's DoWork event, which runs in a separate thread.
But the variables inside that form(class level scope) is accessible inside it. Isn't it ?
I just tried it by declaring a class level array and inside the DoWork, I have returned the length of the array.
So, any variables declared in that current form is accessible, right?
Or that's not the best approach? I should always pass the values to it as a parameter of RunWorkerAsync() ?
Thanks
If my post was helpful to you, then express your gratitude using Rate this Post.
And if your problem is SOLVED, then please Mark the Thread as RESOLVED (see it in action - video) My system: AMD FX 6100, Gigabyte Motherboard, 8 GB Crossair Vengance, Cooler Master 450W Thunder PSU, 1.4 TB HDD, 18.5" TFT(Wide), Antec V1 Cabinet Social Group:VBForums - Developers from India
jm, all controls are in the UI thread. And thus, we can't access it from the BackgroundWorker's DoWork event, which runs in a separate thread.
But the variables inside that form(class level scope) is accessible inside it. Isn't it ?
I just tried it by declaring a class level array and inside the DoWork, I have returned the length of the array.
So, any variables declared in that current form is accessible, right?
Or that's not the best approach? I should always pass the values to it as a parameter of RunWorkerAsync() ?
Thanks
You're confusing two unrelated things. The DoWork event handler of the BackgroundWorker is a method like any other other method. It is a member of the form like any other form. It has all the same rights and privileges as any other method in the form. Member variables are within the scope of the form so any methods within that form can access those methods.
You can even write code in that method to access the controls on the form and the compiler will not complain as long as the syntax is correct. If you were to call that method directly then the code would work fine. It's only if, at run time, a method is run on a secondary thread that any control access that uses the Handle will fail. That's a run time issue, not a design time issue.
You're confusing two unrelated things. The DoWork event handler of the BackgroundWorker is a method like any other other method. It is a member of the form like any other form. It has all the same rights and privileges as any other method in the form. Member variables are within the scope of the form so any methods within that form can access those methods.
You can even write code in that method to access the controls on the form and the compiler will not complain as long as the syntax is correct. If you were to call that method directly then the code would work fine. It's only if, at run time, a method is run on a secondary thread that any control access that uses the Handle will fail. That's a run time issue, not a design time issue.
Means, the methods that access the controls can't be called inside the DoWork() as well as no controls can be accessed directly within that event.
Because, the code inside the DoWork() event executes in a separate thread and the controls are in another thread.
But all other variables/methods can directly be accessed within in it.
Am I correct ?
Thanks
EDIT:
jm, I am still confused(even after reading the below post). So, I will try to refresh my brain by reading again topics about Threading and thread affinity. And will create a thread in VB.Net forum, instead of flooding this thread Thanks again.
Last edited by akhileshbc; Jul 26th, 2012 at 01:16 AM.
Reason: -- @ JM --
If my post was helpful to you, then express your gratitude using Rate this Post.
And if your problem is SOLVED, then please Mark the Thread as RESOLVED (see it in action - video) My system: AMD FX 6100, Gigabyte Motherboard, 8 GB Crossair Vengance, Cooler Master 450W Thunder PSU, 1.4 TB HDD, 18.5" TFT(Wide), Antec V1 Cabinet Social Group:VBForums - Developers from India
Means, the methods that access the controls can't be called inside the DoWork() as well as no controls can be accessed directly within that event.
Because, the code inside the DoWork() event executes in a separate thread and the controls are in another thread.
But all other variables/methods can directly be accessed within in it.
Am I correct ?
Thanks
No you're not correct. Controls are accessed via member variables too. All members are accessible from all other members. Accessing a control in the DoWork event handler is allowed. The compiler will not complain. At run time though, those accesses that are performed via legal code will fail. They are two different concepts: scope and thread affinity.