Beginners question on threading

jvcoach23

Centurion
Joined
May 22, 2003
Messages
192
Location
Saybrook, IL
I've been doing some research on Threading and am trying to give it a go. In my sample code, i have everything working except the return values are not getting passed back to the parent thread to be displayed in the list box. the debug.writeline tells me I'm getting the database back.. but can't get it to the parent thread. what am I doing wrong.

Visual Basic:
Option Strict Off
Imports System.Threading
Imports System.Data
Imports System.Data.SqlClient

Public Class Form3
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub
    Dim t1 As Thread
    Dim t2 As Thread
    Dim WithEvents oSquare As SquareClass

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim oSquare1 As New SquareClass
        Dim oSquare2 As New SquareClass

        t1 = New Thread(AddressOf oSquare1.CalcSquare)
        t1.Name = "Data"
        oSquare1.vcTableName = "tblPMData"


        t2 = New Thread(AddressOf oSquare2.CalcSquare)
        t2.Name = "Company"
        oSquare2.vcTableName = "tblPMCompany"

        t1.Start()
        t2.Start()

        If t1.Join(500) Then
            Me.ListBox1.Items.Add(oSquare1.Result)
        End If

        If t2.Join(500) Then
            Me.ListBox2.Items.Add(oSquare2.Result)
        End If
    End Sub
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        t1.Abort()
    End Sub
    Public Class SquareClass
        Private mvalue As Integer
        Private msquare As Double
        Private mvcTableName As String
        Private mresult As Integer

        Public Event threadcomplete(ByVal Result As Integer)
        Public Property vcTableName() As String
            Get
                Return mvcTableName
            End Get
            Set(ByVal pValue As String)
                mvcTableName = pValue
            End Set
        End Property
        Public Property Result() As Integer
            Get
                Return mresult
            End Get
            Set(ByVal pValue As Integer)
                mresult = pValue
            End Set
        End Property

        Public Property value1() As Integer
            Get
                Return mvalue
            End Get
            Set(ByVal pValue As Integer)
                mvalue = pValue
            End Set
        End Property
        Public Property square() As Double
            Get
                Return (msquare)
            End Get
            Set(ByVal pSquare As Double)
                msquare = pSquare
            End Set
        End Property
        Public Sub CalcSquare()
            SyncLock GetType(SquareClass)
                Dim cn As SqlConnection
                Dim cm As New SqlCommand
                Dim result As Integer


                cn = New SqlConnection("pwd=idontusesa; UID=sa; server=Ntdts1; database=Monitor")
                cm = New SqlCommand

                With cm
                    .Connection = cn
                    .CommandType = CommandType.Text
                    .CommandText = "select count(1) from " & vcTableName
                End With

                cn.Open()
                Try
                    result = cm.ExecuteScalar
                Catch ex As Exception
                    Throw ex
                End Try

                cn.Close()

                Debug.WriteLine(result)
                'MsgBox(vcTableName & " had " & CType(result, String) & " rows")

                RaiseEvent threadcomplete(result)
            End SyncLock
        End Sub
    End Class
    Private Sub oSquare_threadcomplete(ByVal Result As Integer) Handles oSquare.threadcomplete
        MsgBox(Result)
    End Sub
 
cant you do smthng like this:

Visual Basic:
Class Square
Public Event Done(Sender as Square)
Dim T as System.Threading.Thread
'
'
'
'
'
'Your properties and whatever else
'
'
Public Sub CalculateSquare()

        T=New System.Threading.Thread(Addressof CalcSquare)
        T.Start

end sub

Private CalcSquare()

'           Do your Stuff

Raiseevent Done(Me)
End Sub

End Class
 
I think you should be setting mresult in your CalcSquare() code, NOT declaring a local result var (which btw has the same name as the public property, a big no-no).
 
Ok.. I have it working (part way). I can't get the raiseevent to fire. I tried to set a break point.. and it hits that break point but never fires off the event. Am I creating the event correctly.

Visual Basic:
Public Class Form4
    Inherits System.Windows.Forms.Form
    Dim WithEvents oSquare1 As SquareClass
    Dim WithEvents oSquare2 As SquareClass


  Public Class SquareClass
        Private mvalue As Double
        Private msquare As Double

        Public Event threadcomplete(ByVal square As SquareClass)

        Public Property value() As Double
            Get
                Return mvalue
            End Get
            Set(ByVal pValue As Double)
                mvalue = pValue
            End Set
        End Property
        Public Property square() As Double
            Get
                Return (msquare)
            End Get
            Set(ByVal pSquare As Double)
                msquare = pSquare
            End Set
        End Property
        Public Sub CalcSquare()
            SyncLock GetType(SquareClass)
                square = value * value
                RaiseEvent threadcomplete(Me)

            End SyncLock
        End Sub
    End Class

    Sub SquareEventHandler(ByVal square As SquareClass) Handles oSquare1.threadcomplete
        MsgBox("Went to the handler")
    End Sub

hope this is all the info needed to help me past the current obstacle.
 
It's been a good day..

I have the threading working.. it also is returing the data back from the thread through an event... The question I have now.. if I start 2 threads.. and the first thread I start takes longer to run then the second thread.. should the second thread have to wait for the first thread to come back before it completes thread 2.

I am passing in a value to each thread.. the first threads value = 3000. in the class that is called it is doing a thread.currentthread.sleep(value). The second thread has a value of 10. it looks like thread 1 always has to complete before thread 2.

If I change the values around.. thread 2 = 3000 and thread 1 = 10, then thread 1 comes back.. than then thread 2 3 seconds later.

one other thing.. do you have to clean up after the thread completes

thanks
 
here you go.. there isn't much code.. so i grabbed it all.
Visual Basic:
 Dim t1 As Thread
    Dim t2 As Thread
    Dim WithEvents oSquare1 As SquareClass
    Private Sub Form4_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Label1.Text = Nothing
        Me.Label2.Text = Nothing
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        oSquare1 = New SquareClass

        t1 = New Thread(AddressOf oSquare1.CalcSquare)
        t1.Name = "tblPMData"
        oSquare1.value = 3000
        Me.Label1.Text = "Thread 1 started Processing"
        t1.Start()
        t1.Sleep(10)

        t2 = New Thread(AddressOf oSquare1.CalcSquare)
        t2.Name = "tblPMCompany"
        oSquare1.value = 10
        Me.Label2.Text = "Thread2 started Processing"
        t2.Start()

        Refresh()
        If t1.ThreadState.Stopped Then
            t1 = Nothing
        End If

        If t2.ThreadState.Stopped Then
            t2 = Nothing
        End If

    End Sub
    Public Class SquareClass
        Private mvalue As Double
        Private msquare As Double

        Public Event threadcomplete(ByVal square As Integer, ByVal thread As String)

        Public Property value() As Double
            Get
                Return mvalue
            End Get
            Set(ByVal pValue As Double)
                mvalue = pValue
            End Set
        End Property
        Public Property square() As Double
            Get
                Return (msquare)
            End Get
            Set(ByVal pSquare As Double)
                msquare = pSquare
            End Set
        End Property
        Public Sub CalcSquare()
            SyncLock GetType(SquareClass)
                square = value * value
                Debug.WriteLine(value)
                Thread.CurrentThread.Sleep(value)
                RaiseEvent threadcomplete(square, Thread.CurrentThread.Name.ToString)
            End SyncLock
        End Sub
    End Class

    Sub SquareEventHandler(ByVal square As Integer, ByVal thread As String) Handles oSquare1.threadcomplete
        Debug.WriteLine(square & " - Ran on thread " & thread)

        If thread = "tblPMData" Then
            Me.ListBox1.Items.Add(square)
            Me.Label1.Text = "Thread 1 finished Processing - " & Now.Second & " - " & Now.Millisecond
        Else
            Me.ListBox2.Items.Add(square)
            Me.Label2.Text = "Thread 2 finished processing - " & Now.Second & " - " & Now.Millisecond

        End If
        Refresh()
    End Sub

End Class
thanks.. shannon
 
What advantages does the adhandler give over using the raiseevent like I was doing in test code... which one should I be using??? From what little I've learned since you asked the question.. it looks like they do the same thing.. but what is the reason for 2 different ways of doing the same thing.. I'm not getting something am I.
thanks
shannon
 
One is declarative, one is imperative syntax. The advantage of using AddHandler is that you can easily have one method handle multiple events (I may be wrong, but I don't think this is possible with the declarative syntax).
 
Actually, with declarative syntax, you can handle multiple events by using a comma-delimited list of those events.

Something like:

Private Sub button_Click(.. blah blah args ..) Handles Button1.Click, Button2.Click, Button3.Click
 
penfold69 said:
Actually, with declarative syntax, you can handle multiple events by using a comma-delimited list of those events.

Something like:

Private Sub button_Click(.. blah blah args ..) Handles Button1.Click, Button2.Click, Button3.Click

Thats true but with the addhandler keyword you can managed controls created on run-time better...

Plus if an object goes out of scope for the function that created it but there is still a referrence to it, the event will still fire.
like if a function in a class creates an object by declaring it only in the scope of that function and then passes it to another class if addhandler was used in order to add an event handler in the first class, when the event fires the handler will be used.

What is interesting is what happens if an event has multiple handlers? With what sequence will the handlers be called?
 
Last edited:
jvcoach23 said:
What advantages does the adhandler give over using the raiseevent like I was doing in test code... which one should I be using??? From what little I've learned since you asked the question.. it looks like they do the same thing.. but what is the reason for 2 different ways of doing the same thing.. I'm not getting something am I.
thanks
shannon

!!!
U still need to use raiseevent with addhandler....
The difference is in the declaration of the object...


Visual Basic:
Public Class MyClass

Public Event SomeEvent()

Public Sub MySub
 RaiseEvent SomeEvent()
End Sub

End Class

AddHandler Style

Visual Basic:
Private Sub Form1_Load(Blah)
Dim MC as new MyClass
addhandler MC.SomeEvent,addressof EventHandler
end sub

Private Sub EventHandler
'Blah
End Sub


With events style

Visual Basic:
Dim WithEvents MC as new MyClass

Private Sub EventHandler Handles MC.SomeEvent

End Sub
 
was playing around some more... with something different within the threads... it appears that if I do a while loop in a thread i start, that the first thread works... going through this loop. However, the second thread I start doesn't appear to even run. So I'm doing to starts, but the threads only work if I take a the while loop inside the class that I call when I create the new thread. can you not do a while loop inside a thread
 
you can definately do a while loop inside a thread. Post your code and show us why you think the second thread isn't being executed.
 
Optikal said:
you can definately do a while loop inside a thread. Post your code and show us why you think the second thread isn't being executed.
the reasons I was thinking it was not firing is the debug.writeline I have in each thread. when I take the while loop out.. i see both debug statements come up.. if I have the while loop.. only one debug.writeline every shows up. The dataset that is being used to put information in for the threads has 2 rows in it.

Visual Basic:
Public Class PerfmonClass
        Private mvcServer As String
        Private mvcCompany As String
        Private mvcCategoryName As String
        Private mvcCounterName As String
        Private mvcInstance As String
        Private mintTblPMInstanceId As Integer

        Public Event PerfmonThread()

        Dim WithEvents oCounter As New PerformanceCounter
        Dim Counter As Integer


        Public Property vcServer() As String
        Public Property vcCompany() As String
        Public Property vcCategoryName() As String
        Public Property vcCounterName() As String
        Public Property vcInstance() As String
        Public Property intTblPMInstanceId() As Integer

        Public Sub GetPerfmon()
            SyncLock GetType(PerfmonClass)

                'setup the counter 
                With oCounter
                    .CategoryName = vcCategoryName
                    .CounterName = vcCounterName
                    .InstanceName = vcInstance
                    .MachineName = vcServer
                    .BeginInit()
                End With

                While 1 = 1
                    'poll the counter for the info
                    Counter = oCounter.NextValue
                    Debug.WriteLine(Counter & " - " & Thread.CurrentThread.Name.ToString)
                    Thread.CurrentThread.Sleep(5000)
                End While
            End SyncLock
        End Sub
    End Class

 Public Sub GetServerCounters()
        oPerfmon = New PerfmonClass

        Dim ds As DataSet
        ds = wsPerfmon.spPMCategoryInfoForOneServer("home", "s011038home")

        Dim dr As DataRow

        For Each dr In ds.Tables(0).Rows
            Dim perfThread = New Thread(AddressOf oPerfmon.GetPerfmon)
            oPerfmon.intTblPMInstanceId = dr.Item("intTblPMInstanceId")
            oPerfmon.vcCategoryName = dr.Item("vcCategoryName")
            oPerfmon.vcCompany = "home"
            oPerfmon.vcCounterName = dr.Item("vcCounterName")
            oPerfmon.vcInstance = dr.Item("vcInstance")
            oPerfmon.vcServer = dr.Item("vcServer")

            perfThread.name = dr.Item("vcCategoryName")
            Debug.WriteLine(dr.Item("vcCounterName"))
            perfThread.start()
            Thread.Sleep(5)
        Next
    End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        GetServerCounters()
    End Sub

right now I wasn't trying to raise any events... in the end.. I'll want this loop to write to a database each time it loops through...

thanks for the help
Shannon
 
Multithreading to my knowledge can be easily mistaken to mean two processes running on the CPU concurrently. This is not the case. The two threads take it in turn to use processor time similar to two separate programs. If the processor is not told to free itself up for the other thread surely you will get one thread hogging all the process time. Having not used the currentthread.sleep I could not say if this frees the processor or not. Maybe an application.doevents in there would free up the processor for the other thread?

Dill
 
Dill said:
If the processor is not told to free itself up for the other thread surely you will get one thread hogging all the process time. Having not used the currentthread.sleep I could not say if this frees the processor or not. Maybe an application.doevents in there would free up the processor for the other thread?

Dill

I believe you are wrong on that...
The OS will automaticly pre-empt threads that are running and give other threads with the same priority a time slice. Only win3.1 had the cooperative multitasking where tasks could claim 100% cpu without being any change of being preempted. That mechanism has been fully abandoned in favor of pre-emptive multitasking (in both winNT and win95, with different levels of succes ;) ) where the OS can give time to other threads of the same priority after some time (20 milliseconds or so, i dont know how much) even though the original thread isnt finished yet.
Note that this is the reason why using multithreading becomes a very hard job. You NEVER KNOWN WHEN your executing thead is pre-empted and another is activated. Making it very VERY important to use the correct thread safety mechanisms like ReaderWriterLock. Many crashes relating to multithreading can never be reproduced because the exact timeing can't be recreated. I can still get Word to crash using background printing on large documents, due to incorrect multithreading in Word (at least Word 2000, havent tried with latest version, but I dont have high hopes).

Only if all other threads have lower priority can a thread keep the cpu occupied (even for such a configuration there are ways that the low prioirty threads do get some cpu time).
The sleep does allow the OS to schedule a different thread. I believe even a sleep time of 0 allows the OS to pre-empt the executing thread and start running another thread. Returning to the origal thread when it has gone through its list of threads.
 
With what Wile has said.. and it sounds like it makes sense, why does this code
Visual Basic:
                While 1 = 1
                    'poll the counter for the info
                    Counter = oCounter.NextValue
                    Debug.WriteLine(Counter & " - " & Thread.CurrentThread.Name.ToString)
                    Thread.CurrentThread.Sleep(5000)
                End While
seem to no allow the second thrid thread to be started. Based on what you are saying.. the first thread.. the one the intial form is loaded with is running, on the button click it loops through and created thread #2 and that runs taking a slight pause when it hits the sleep command... but thread #3 never does fire.. it sounds like it should fire and start #3 regardlesss of the thread sleep command.. seeing how is it suppose to only sleep on the current thread...#2
 
jvcoach23 said:
With what Wile has said.. and it sounds like it makes sense, why does this code
...
seem to no allow the second thrid thread to be started. Based on what you are saying.. the first thread.. the one the intial form is loaded with is running, on the button click it loops through and created thread #2 and that runs taking a slight pause when it hits the sleep command... but thread #3 never does fire.. it sounds like it should fire and start #3 regardlesss of the thread sleep command.. seeing how is it suppose to only sleep on the current thread...#2

I don't know how exactly the vb SyncLock works, but my guess is that it works the same as the lock statement in C#. The trick is that you pass it something (in your case you pass it the result of GetType(PerfmonClass)), and that every other SyncLock statement that is passed the same argument is halted, until the first one to lock on that particular object, unlocks it.
Now in your case, the first thing you do is get a SyncLock in the GetPerfMon method, and you dont release it until the method is completely done, including the while loop. This means that once the first thread gets the SyncLock, all the other threads, will be waiting until the first thread unlocks, which is never as you loop forever.

You can test this by putting a counter increment just before the synclock (Use the System.Threading.Interlocked.Increment method for this to make sure it is thread safe) and that counter should increase. You can even put a break point on that counter and step through the code, you will see that the 2nd and next threads will wait for the SyncLock method.

The idea of the SyncLock is correct, you want to protect the member variables of the class from simultanious access by multiple threads, however, in the while loop, during the sleep, there is no need to keep the SyncLock. I suggest you take a good look at that loop and decide where the SyncLock is really needed to protect the member variables, and where it isn't needed, e.g. the sleep. If the lock is not needed, unlock it so other threads can enter.

You might want to take a look at the ReaderWriterLock as it allows more control over how member variables are used (allowing multiple reads at the same time, but makes sure that only 1 thread can write at a time, without any thread reading).
 
thanks a million for the lesson.. I'll have to read it a couple times and try to get it goign tonight... thanks again for the explinations.

Shannon
 
Back
Top