ZhenyaT
asked on
Multithreading
I have a program that listens to MSMQ messages and preforms database updates depending on messages. I want to build a monitor program that would display the arrived message on a form and also provide the user with ability to click on the buttons on this form at ANY time (even when my component is listening for MSMQ messages. I assume I have to put this component in a separate thread from the monitor application. How do I do it? I need details explanation and possible links with examples.
Thank you
Thank you
ASKER
CJ:
I have the MSMQ working and that's what ive done - an out-of-process component. Unfortunatelly it doesnt work! I must be doing something wrong. What happens is that as soon as the outofprocess component enters say a loop, my monitor client blocks! When I click on it, windows brings up a message with switch to and retry options.
I have the MSMQ working and that's what ive done - an out-of-process component. Unfortunatelly it doesnt work! I must be doing something wrong. What happens is that as soon as the outofprocess component enters say a loop, my monitor client blocks! When I click on it, windows brings up a message with switch to and retry options.
What you need to do is make the out of process component return immediately when handling the event. I usually achieve this type of thing by using a timer which i set active in the event handler and then return from the call. Whatever happens in the timer event will not block your other components. I can provide you with a 'code only' timer (messing around with forms and control is risky in this type of component).
Francois.
Francois.
Make it an ActiveX EXE... and connect to it using GetObject("", "BLAH.BLOEH")
Regards,
CJ
Regards,
CJ
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Here is an example of multithreading in VB. This program reads 3 text files on 3 different threads. I added a project reference to Microsoft Scripting Runtime to use File System Objects to access the files.
Timers on forms are used to kick off the threads asynchronously so the files process at the same time.
A form is also used to identify the first time though, since main is automatically called every time
a thread is created.
Even though the threads in this example process the files "independently", any communication between threads is still syncronized via COM which reduces application responsiveness. If you are interested I can post an example of communicating between threads outside of COM control but in that case you will have to ensure that your threads don't update variables simultaneously.
VB 6 is not very friendly to multithreading applications.
========================== ========== ==========
' MainModule.bas
' ActiveX EXE Name = MTNF
' Startup Object = Sub Main
' Threading Model = Thread per Object
' Start Mode = Standalone
' !!! Must compile and run outside IDE !!!
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal
lpWindowName As String) As Long
Private frmMainThreadForm As MainThreadForm
Private Const PROC_CAPTION = "MTNF Main Thread Form"
Private Thread1 As ProcessFileObject
Sub Main()
If 0 = FindWindow(vbNullString, PROC_CAPTION) Then
'MsgBox "Main = " & Hex(App.ThreadID)
Set frmMainThreadForm = New MainThreadForm ' create first form on pre-existing thread
frmMainThreadForm.Caption = PROC_CAPTION
Set frmMainThreadForm = Nothing 'don't need this reference, form loads itself and stays alive
until unloaded
End If
End Sub
Public Sub CreateThreads()
Set Thread1 = CreateObject("MTNF.Process FileObject ")
Thread1.ProcessFile "a.txt"
'
Set Thread1 = CreateObject("MTNF.Process FileObject ")
Thread1.ProcessFile "b.txt"
'
Set Thread1 = CreateObject("MTNF.Process FileObject ")
Thread1.ProcessFile "c.txt"
Set Thread1 = Nothing
End Sub
Public Sub ReportRecordCount(FileName As String, RecCount As Long)
MsgBox FileName & " contains " & CStr(RecCount) & " records. Reported by Thread " & Hex(App.ThreadID)
End Sub
========================== ========== ==========
' MainThreadForm.frm
Private Sub Form_Initialize()
Timer1.Interval = 100
End Sub
Private Sub Timer1_Timer()
Timer1.Enabled = False
CreateThreads
Unload Me
End Sub
========================== ========== ==========
' ProcessFileObject.cls
Private frmTimerForm As TimerForm
Private Sub Class_Initialize()
'MsgBox "Worker = " & Hex(App.ThreadID)
End Sub
Public Sub ProcessFile(FileName As String)
Set frmTimerForm = New TimerForm
frmTimerForm.SetOwner Me
frmTimerForm.StartTimer FileName
End Sub
Public Sub RecordCount(FileName As String)
' add project reference to Microsoft Scripting Runtime
' to use File System Objects
Dim fso As New FileSystemObject
Dim fil1 As File
Dim ts As TextStream
Set fil1 = fso.GetFile(FileName)
Set ts = fil1.OpenAsTextStream(ForR eading)
ts.ReadAll
ReportRecordCount FileName, ts.Line
ts.Close
Set frmTimerForm = Nothing
End Sub
========================== ========== ==========
' TimerForm.frm
Private objOwner As ProcessFileObject
Private strFileName As String
Public Sub SetOwner(objRef As ProcessFileObject)
Set objOwner = objRef
End Sub
Public Sub StartTimer(FileName As String)
strFileName = FileName
Timer1.Interval = 55
End Sub
Private Sub Timer1_Timer()
Timer1.Enabled = False
objOwner.RecordCount (strFileName)
Set objOwner = Nothing
Unload Me
End Sub
Timers on forms are used to kick off the threads asynchronously so the files process at the same time.
A form is also used to identify the first time though, since main is automatically called every time
a thread is created.
Even though the threads in this example process the files "independently", any communication between threads is still syncronized via COM which reduces application responsiveness. If you are interested I can post an example of communicating between threads outside of COM control but in that case you will have to ensure that your threads don't update variables simultaneously.
VB 6 is not very friendly to multithreading applications.
==========================
' MainModule.bas
' ActiveX EXE Name = MTNF
' Startup Object = Sub Main
' Threading Model = Thread per Object
' Start Mode = Standalone
' !!! Must compile and run outside IDE !!!
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal
lpWindowName As String) As Long
Private frmMainThreadForm As MainThreadForm
Private Const PROC_CAPTION = "MTNF Main Thread Form"
Private Thread1 As ProcessFileObject
Sub Main()
If 0 = FindWindow(vbNullString, PROC_CAPTION) Then
'MsgBox "Main = " & Hex(App.ThreadID)
Set frmMainThreadForm = New MainThreadForm ' create first form on pre-existing thread
frmMainThreadForm.Caption = PROC_CAPTION
Set frmMainThreadForm = Nothing 'don't need this reference, form loads itself and stays alive
until unloaded
End If
End Sub
Public Sub CreateThreads()
Set Thread1 = CreateObject("MTNF.Process
Thread1.ProcessFile "a.txt"
'
Set Thread1 = CreateObject("MTNF.Process
Thread1.ProcessFile "b.txt"
'
Set Thread1 = CreateObject("MTNF.Process
Thread1.ProcessFile "c.txt"
Set Thread1 = Nothing
End Sub
Public Sub ReportRecordCount(FileName
MsgBox FileName & " contains " & CStr(RecCount) & " records. Reported by Thread " & Hex(App.ThreadID)
End Sub
==========================
' MainThreadForm.frm
Private Sub Form_Initialize()
Timer1.Interval = 100
End Sub
Private Sub Timer1_Timer()
Timer1.Enabled = False
CreateThreads
Unload Me
End Sub
==========================
' ProcessFileObject.cls
Private frmTimerForm As TimerForm
Private Sub Class_Initialize()
'MsgBox "Worker = " & Hex(App.ThreadID)
End Sub
Public Sub ProcessFile(FileName As String)
Set frmTimerForm = New TimerForm
frmTimerForm.SetOwner Me
frmTimerForm.StartTimer FileName
End Sub
Public Sub RecordCount(FileName As String)
' add project reference to Microsoft Scripting Runtime
' to use File System Objects
Dim fso As New FileSystemObject
Dim fil1 As File
Dim ts As TextStream
Set fil1 = fso.GetFile(FileName)
Set ts = fil1.OpenAsTextStream(ForR
ts.ReadAll
ReportRecordCount FileName, ts.Line
ts.Close
Set frmTimerForm = Nothing
End Sub
==========================
' TimerForm.frm
Private objOwner As ProcessFileObject
Private strFileName As String
Public Sub SetOwner(objRef As ProcessFileObject)
Set objOwner = objRef
End Sub
Public Sub StartTimer(FileName As String)
strFileName = FileName
Timer1.Interval = 55
End Sub
Private Sub Timer1_Timer()
Timer1.Enabled = False
objOwner.RecordCount (strFileName)
Set objOwner = Nothing
Unload Me
End Sub
ASKER
CJ:
I have the MSMQ working and that's what ive done - an out-of-process component. Unfortunatelly it doesnt work! I must be doing something wrong. What happens is that as soon as the outofprocess component enters say a loop, my monitor client blocks! When I click on it, windows brings up a message with switch to and retry options.
I have the MSMQ working and that's what ive done - an out-of-process component. Unfortunatelly it doesnt work! I must be doing something wrong. What happens is that as soon as the outofprocess component enters say a loop, my monitor client blocks! When I click on it, windows brings up a message with switch to and retry options.
ASKER
Cheers dude, awesome idea...dunno why I didn't think of it...
one thing you missed out is you need to receive the message every time it arrives and then reset the EnableNotification.
Thanks a lot.
one thing you missed out is you need to receive the message every time it arrives and then reset the EnableNotification.
Thanks a lot.
Public Event MyEvent(...)
and raise it using
RaiseEvent MyEvent(the paramaters necessary)
Then in your client application you use something like:
Public WithEvents MyVar as COMPPROJECT.CLASSNAME
Private Sub MyVar_MyEvent(...)
End Sub
How to monitor the MSMQ messages I have no idea, but I suppose you can somehow retrieve them...no experience in that part)
regards,
CJ