Question Threading and Events

Adagio

Well-known member
Joined
Dec 12, 2005
Messages
162
Programming Experience
Beginner
What would be the best way to solve this problem:

I have a class (let's call it TestClass) and a form. When I create an instance of TestClass it starts a thread, which runs constantly in the background. Now and then the background-thread raises an event, which the form listens for. When the event is raised, the form should update some controls on the form
Anyone who has ever worked with threads, know what probably would happen: Cross-thread operation error

Here is the code I have:

VB.NET:
Public Class Form1

    Private WithEvents c As New TestClass

    Private Delegate Sub UpdateUIDelegate(ByVal index As Integer)

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'AddHandler c.CountChanged, AddressOf CountChanged
        c.Count()
    End Sub

    Private Sub CountChanged(ByVal index As Integer) Handles c.CountChanged
        ProgressBar1.Value = index
    End Sub

End Class

Public Class TestClass

    Public Sub Count()
        Dim t As New Threading.Thread(AddressOf CountingTest)
        t.Start()
    End Sub

    Private Sub CountingTest()
        Dim index As Integer = 0


        While True
            index += 1

            RaiseEvent CountChanged(index)

            Threading.Thread.Sleep(1000)
        End While
    End Sub

    Public Event CountChanged(ByVal index As Integer)

End Class

Now before anyone comes with the normal responses, that you normally read as answers in these questions, I would like to say that I know at least one way to solve this problem:

In the CountChanged sub on the form it can be changed to this:

VB.NET:
 Private Delegate Sub UpdateUIDelegate(ByVal index As Integer)




	If Me.InvokeRequired Then
            Dim Parms() As Object = {index}
            Me.Invoke(New UpdateUIDelegate(AddressOf CountChanged), Parms)
        Else
            ProgressBar1.Value = index
        End If

It's a solution, but IMHO not a good solution, I'm looking for a more userfriendly solution
It would be better if you didn't have to do this on the form, so that those who uses my class shouldn't have to worry about Threads. What would be the best way to solve this problem?
 
There are some options, you can pass reference of a UI control to the class and use it as "synchronizing object" for event delegation, for example System.Timers.Timer class uses this approach. BackGroundWorker can also be used within the class to do async work, where you post UI-safe events from the ProgressChanged/RunWorkerCompleted events of the worker. BackgroundWorker uses functionality from AsyncOperation/-Manager classes internally, so you can also use these instead of Bgw.

Several class library methods carry Async/BeginWork naming and do raise events/callbacks from the new thread that was created, this enables better correlation and performance for consumers that operates on threading and not in UI. In other words, if user initiates an async method then user expect it to callback on other thread, and may choose to invoke from there. So it is perhaps better to provide option for an synchronizing object than to force it.
 
I'll take a close look at the AsyncOperation/-Manager classes

I don't like the backgroundworker, it has proven not to be reliable with it's Events
I would prefer not to pass a reference of a UI control to this class, as the class should be useable for different types of UI controls

The class I'm making is only used for background work. It works in the background from the second it is created to the second it is disposed. Based on what it's working on, it can raise several types of events, not just a "x% of the work completed" event

What I'm trying to accomplish is to remove redundant code. Instead of writing 6 extra lines of code for each time an event is raised (for each form), I only want to write these extra lines of code inside the class that fires these events (or something like that), to make it simpler for those who uses the code (those who uses it shouldn't worry about how it does it's work, as longs as it does it)
Kinda like let's take this crazy example:
Someone creates a custom button, that as soon as you click on it a new thread is created and this thread (after some other pointless work) raises the ButtonClicked event. In a case like this, it wouldn't make sense to force the programmers, who uses this custom button, to use invoke (or whatever) in the sub that handles the event
 
Back
Top