Results 1 to 7 of 7

Thread: Alternatives to the BackgroundWorker?

  1. #1
    Addicted Member
    Join Date
    Aug 11
    Posts
    200

    Alternatives to the BackgroundWorker?

    Some of you may remember my thread I posted a couple weeks ago looking for an alternative to application.doevents. For those that don't I foolishly started using application.doevents in my project, to prevent the form from locking up, well it worked great until I started adding more features to the program, then I started getting so many odd bugs, everytime I found a workaround for the bugs, it would cause the program to bug out somewhere else.

    So the background worker was suggested, although in my case I can't use it. I have a class and in the class I have a sub that I call from a seperate form. When the sub is called it starts going through a loop that is completing different processes. This class sub has to access controlson this form. Such as a selecting items in a listview, parsing items in a listview, enabling buttons etc, and I also need to keep my parameters on this sub.

    So basically I foundout I can't use the background worker. So what is an alternative to this? I need to do something I can't stand seeing the application I worked so hard on having al the bugs from .doevents. Also it would be too much work to go back and restructure the application.

  2. #2
    .NUT jmcilhinney's Avatar
    Join Date
    May 05
    Location
    Sydney, Australia
    Posts
    80,755

    Re: Alternatives to the BackgroundWorker?

    This issue is simple. You have some work that needs to be done in the background so as to maintain a responsive UI but you also have some UI updates interspersed in that work and they cannot be done in the background. Background work means multi-threading but all UI access must be done on the UI thread. No matter what you end up doing, you're going to need to change your design. The design you have now is obviously bad, and that's the root of your troubles. You say that you have this work going on in a separate class but then that class is directly accessing controls on a form. Not good. If you're accessing controls on the form then everything may as well be in the form because you're not making anything cleaner or easier.

    Basically, if you try to keep things the way they are but get them to work then you're in for a world of pain. The best option would be to redesign the whole thing properly. Put the non-visual work into a class dedicated to that work and keep all the visual stuff in the form. Your class can do its work on a separate thread and then raise events whenever the UI needs to be updated. The form can then handle those events and the appropriate updating. An example of a class that does work in the background and raises events for UI updates is... the BackgroundWorker. You can either use a BackgroundWorker inside your class or you can implement your class such that it works in a similar way to the BackgroundWorker. Here is a simple example of such a class:
    vb.net Code:
    1. Public Class AsyncThing
    2.  
    3.     Private context As SynchronizationContext = SynchronizationContext.Current
    4.  
    5.     Public Event DoingSomething As EventHandler
    6.     Public Event DoneSomething As EventHandler
    7.  
    8.     Public Sub DoSomething()
    9.         Dim t As New Thread(AddressOf DoSomethingCore)
    10.  
    11.         t.Start()
    12.     End Sub
    13.  
    14.     Private Sub DoSomethingCore()
    15.         'Do some work.
    16.         Thread.Sleep(5000)
    17.  
    18.         'Raise the DoingSomething event on the thread that created the current instance.
    19.         context.Post(AddressOf RaiseDoingSomething, Nothing)
    20.  
    21.         'Do some work.
    22.         Thread.Sleep(5000)
    23.  
    24.         'Raise the DoingSomething event on the thread that created the current instance.
    25.         context.Post(AddressOf RaiseDoingSomething, Nothing)
    26.  
    27.         'Do some work.
    28.         Thread.Sleep(5000)
    29.  
    30.         'Raise the DoneSomething event on the thread that created the current instance.
    31.         context.Post(AddressOf RaiseDoneSomething, Nothing)
    32.     End Sub
    33.  
    34.     Protected Overridable Sub OnDoingSomething(e As EventArgs)
    35.         RaiseEvent DoingSomething(Me, e)
    36.     End Sub
    37.  
    38.     Protected Overridable Sub OnDoneSomething(e As EventArgs)
    39.         RaiseEvent DoneSomething(Me, e)
    40.     End Sub
    41.  
    42.     Private Sub RaiseDoingSomething(dummy As Object)
    43.         OnDoingSomething(EventArgs.Empty)
    44.     End Sub
    45.  
    46.     Private Sub RaiseDoneSomething(dummy As Object)
    47.         OnDoneSomething(EventArgs.Empty)
    48.     End Sub
    49.  
    50. End Class

  3. #3
    Addicted Member
    Join Date
    Aug 11
    Posts
    200

    Re: Alternatives to the BackgroundWorker?

    Thanks JMC I was hoping you would come back here. I like the idea of the class example you posted.

    So here is my only question. If I was to get rid of the code that accesses the form directly, Would I be able to make a call back to the main form, to an event from this class in the donesomething example you posted. Kind of like this.

    I have an event in my form like this

    vb Code:
    1. private sub DoneMyClassWork()
    2. button1.enabled = true
    3. button2.enabled = false
    4. end sub


    And in my class I call that sub from the doneevents?

    vb Code:
    1. Private Sub RaiseDoneSomething(dummy As Object)
    2.         OnDoneSomething(EventArgs.Empty)
    3.         DoneMyClassWork()
    4.     End Sub

    Sorry if I got it all wrong. Still kinda new to me.

  4. #4
    .NUT jmcilhinney's Avatar
    Join Date
    May 05
    Location
    Sydney, Australia
    Posts
    80,755

    Re: Alternatives to the BackgroundWorker?

    That's not an event. It's just a method and you are calling that method from this other class and that's bad design. The point is that the other class simply raises the event and doesn't care beyond that. The form handles the event and then does it's own work. That means that you form has a handler for the DoneSomething event of your object and in that event handler it enables its own Buttons. The UI work is done in the UI, i.e. the form. The object responsible for the background work does ONLY the background work.

  5. #5
    Addicted Member
    Join Date
    Aug 11
    Posts
    200

    Re: Alternatives to the BackgroundWorker?

    Awesome, I understand now. Thanks for the help.

  6. #6
    Addicted Member
    Join Date
    Aug 11
    Posts
    200

    Re: Alternatives to the BackgroundWorker?

    I have one question. I moved everything over to my main form, and I am using the background worker now. My UI no longer hangs, but I am having a hard time cancelling the thread when the cancel button is clicked on my UI.

    I think the button is working properly, I just don't think it is shutting down the thread quick enough. I would like for it to abort the thread right when the button is clicked. The thread isnt too extensive so I am not sure why it isn't I must be doing something wrong.

    I am going to post some examples of the way I am doing it.

    vb Code:
    1. Public Sub New()
    2.         InitializeComponent()
    3.  
    4.         bw.WorkerReportsProgress = True
    5.         bw.WorkerSupportsCancellation = True
    6.         AddHandler bw.DoWork, AddressOf bw_DoWork
    7.         ' AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
    8.         AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
    9.     End Sub

    My DoWork event

    vb Code:
    1. Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
    2.  
    3.         If bw.CancellationPending = True Then
    4.             e.Cancel = True
    5.             WorkEventRunning = False
    6.            
    7.         Else
    8.  
    9.             CheckForIllegalCrossThreadCalls = False
    10.             Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
    11.             'my long winded event
    12.              ' more of my long winded event.
    13.  
    14.        End if

    My Cancel Button code:

    vb Code:
    1. Private Sub ToolStripButton2_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton2.Click
    2.         'Stop
    3.         If bw.WorkerSupportsCancellation = True Then
    4.             WorkEventRunning = False
    5.             bw.CancelAsync()
    6.             bw.Dispose()
    7.  
    8.         End If

    And my workcompleted

    vb Code:
    1. Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
    2.         If e.Cancelled = True Then
    3.             'canceled
    4.             WorkEventRunning = False
    5.         ElseIf e.Error IsNot Nothing Then
    6.             MsgBox(e.Error.Message)
    7.         Else
    8.             'done
    9.         End If
    10.     End Sub

  7. #7
    .NUT jmcilhinney's Avatar
    Join Date
    May 05
    Location
    Sydney, Australia
    Posts
    80,755

    Re: Alternatives to the BackgroundWorker?

    First things first, why are you setting CheckForIllegalCrossThreadCalls? There is no reason to be doing so because the only places you should be updating the UI is in the ProgressChanged and RunWorkerCompleted event handlers, which are executed on the UI thread. Your code implies that you might be updating the UI directly from inside the DoWork event handler or a method called from the DoWork event handler. Get rid of that line and run the code again and if you are updating the UI on the wrong thread then an exception will be thrown and you know where you need to change your code.

    As for the question, the issue is that you are only checking for cancellation once at the very start of the DoWork event handler. If the user requests cancellation after that it will do them no good because you never check for it. When the user requests a cancellation, that's all that happens: it is requested. It's up to you to check for that request repeatedly because it's only by checking that you will detect and only if you detect can you actually cancel. For instance, if you have a loop then it's up to you to check for a cancellation request on each iteration and, if you detect a request then you must end the loop explicitly. If you don't have a loop then it's up to you to find waypoints in your code that represent logical places for you to check for a cancellation request and end the background process if you find one.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •