How to make a thread wait until another thread finish? - vb.net

Static lock As Object
SyncLock lock
TraverSingweb.TraverSingWeb.WebInvoke(Sub() TraverSingweb.TraverSingWeb.putHtmlIntoWebBrowser(theenchancedwinclient)) 'This quick function need to finish before we continue
End SyncLock
SyncLock lock
'SuperGlobal.lockMeFirst(AddressOf SuperGlobal.doNothing) ' donothing
End SyncLock
This is how I currently do in vb.net. Is this a pattern?

Here is a really basic example; the main method simply creates two threads and starts them. The first thread waits 10 seconds and then sets the _WaitHandle_FirstThreadDone. The second thread simply waits for the _WaitHandle_FirstThreadDone to be set before continuing. Both threads are started at the same time but the second thread will wait for the first thread to set the _WaitHandle_FirstThreadDone before continuing on.
See: System.Threading.AutoResetEvent for further details.
Module Module1
Private _WaitHandle_FirstThreadDone As New System.Threading.AutoResetEvent(False)
Sub Main()
Console.WriteLine("Main Started")
Dim t1 As New Threading.Thread(AddressOf Thread1)
Dim t2 As New Threading.Thread(AddressOf thread2)
t1.Start()
t2.Start()
Console.WriteLine("Main Stopped")
Console.ReadKey()
End Sub
Private Sub Thread1()
Console.WriteLine("Thread1 Started")
Threading.Thread.Sleep(10000)
_WaitHandle_FirstThreadDone.Set()
Console.WriteLine("Thread1 Stopped")
End Sub
Private Sub thread2()
Console.WriteLine("Thread2 Started")
_WaitHandle_FirstThreadDone.WaitOne()
Console.WriteLine("Thread2 Stopped")
End Sub
End Module

Related

Multithreading doesn't work

I'm making a simple multithreading program to explain the working of threading. I want two counters counting on the same time but it doesn't work.
It only works if I use: CheckForIllegalCrossThreadCalls = False. But, I want to program in a proper way.
Code:
Dim Thread1 As System.Threading.Thread
Dim Thread2 As System.Threading.Thread
Private Delegate Sub SetTeller1()
Private Sub teller1()
If teller1Label.InvokeRequired Then
Invoke(New SetTeller1(AddressOf teller1))
Else
For i As Integer = 0 To 1000
teller1Label.Text = i
Refresh()
Next
End If
End Sub
Delegate Sub SetTeller2()
Private Sub teller2()
If teller2Label.InvokeRequired Then
Invoke(New SetTeller2(AddressOf teller2))
Else
For i As Integer = 0 To 1000
teller2Label.Text = i
Refresh()
Next
End If
End Sub
Private Sub teller1Button_Click(sender As Object, e As EventArgs) Handles teller1Button.Click
Thread1 = New Threading.Thread(AddressOf teller1)
Thread1.Start()
End Sub
Private Sub teller2Button_Click(sender As Object, e As EventArgs) Handles teller2Button.Click
Thread2 = New Threading.Thread(AddressOf teller2)
Thread2.Start()
End Sub
The multithreading works perfectly, but you are not utilizing it. The only thing you're currently doing in the background thread is calling Invoke, which means that your thread will exit within a few milliseconds and then be discarded.
Once you call Invoke the execution of the teller1 or teller2 method is moved to the UI thread, meaning it will block the UI until its execution is finished. You should only invoke when you are to update the UI, and perform all iterations in the background thread.
Here's an example of how you can do it more properly:
Delegate Sub SetTeller1(ByVal Text As String)
Private Sub teller1()
For i As Integer = 0 To 1000
SetTeller1Text(i)
Next
End Sub
Private Sub SetTeller1Text(ByVal Text As String)
If Me.InvokeRequired Then
Me.Invoke(New SetTeller1(AddressOf SetTeller1Text), Text)
Else
teller1Label.Text = Text
Me.Refresh()
End If
End Sub
For improved readability I changed for example Invoke(...) to Me.Invoke(...).
Also I'm not sure why you're calling Refresh() as it isn't necessary and will just cause extra redrawing of the entire container (guessing this is a form).

Closing a thread if its open after 10 seconds

I have a thread I start like this:
Dim documentConverterThread As New Threading.Thread(AddressOf ConvertToLatestVersion)
documentConverterThread.Start(oDoc)
And this is the function the word document is passed to:
Private Sub ConvertWordTemplateToLatestVersion(ByVal oDoc As Word.Document)
Try
oDoc.Convert()
oDoc.Save()
oDoc.Close()
Catch ex As Exception
End Try
End Sub
What I'm trying to do is that if execution gets stuck when calling the .Convert() function, then close the thread and the word document.
I have been trying to use a timer, but I need access to both the documentConverterThread and oDoc objects to handle the Timer.Tick event:
Private Sub TimerEventProcesser(ByVal sender As Object, ByVal e As System.EventArgs)
If documentConverterThread.IsAlive() Then
documentConverterThread.Abort()
oDoc.Close()
End If
End Sub
Is there any way around this other than using a private variable in the TimerEventProcessor function? Any help is appreciated.
You could create another thread for it. Using the Sub() lambda you can create an inline delegate from which you can call the new timer method and pass both the thread variable and the document variable to it. The new thread will then wait 10 seconds, and if the first thread isn't finished it will close the document.
Private Sub ConvertWordTemplateToLatestVersion(ByVal oDoc As Word.Document)
Dim TimerThread As New Threading.Thread(Sub() TimerThread(documentConverterThread, oDoc))
TimerThread.Start() 'Start the timer thread.
Try
oDoc.Convert()
oDoc.Save()
oDoc.Close()
Catch 'Why catch an exception if we do not even handle it?
End Try
End Sub
Private Sub TimerThread(ByRef dcThread As Threading.Thread, ByRef oDoc As Word.Document)
Threading.Thread.Sleep(10000) 'Wait 10 seconds.
If dcThread.IsAlive() Then 'Close the document if the primary thread isn't finished yet.
dcThread.Abort()
Try
oDoc.Close()
oDoc.Dispose()
Catch
End Try
End If
End Sub
I used ByRef instead of ByVal to be sure we are referencing the same objects all the time and not newly created ones.

Thread in for-loop

How do I know if a thread is finished when I create it in a loop?
Simplified code:
Public Sub LoadExcelFiles()
AddMore:
Dim t As New Thread(New ThreadStart(AddressOf CreateExcelFile))
t.Start()
'Check if there are more files.
'If yes: GoTo AddMore
End Sub
How do I know when thread 't' is completed? I want to add the file created by 't' into a treeview.
Another problem is, when the user drops 33 files, I have 11 threads running at the same time (3 excelfiles are used per thread)
You could consider doing it like this:
Delegate Sub CreateExcelFileAsync()
Public Sub LoadExcelFiles()
Dim d As New CreateExcelFileAsync(AddressOf CreateExcelFile)
Dim result As IAsyncResult = d.BeginInvoke(Nothing, Nothing)
'..
'more work
'..
'wait until completed before starting over
If Not result.IsCompleted() Then
result.AsyncWaitHandle.WaitOne()
End If
d.EndInvoke(result)
End Sub
This way you can use the AsyncResult to check the completion.
An interesting article about the answer of #Paul Deen (wish is a good solution) can be found at http://tech.xster.net/tips/multi-threading-in-vb-net/
Tho I did it another way.
Dim t As New Thread(New ThreadStart(AddressOf CreateExcelFile))
If t.IsAlive Then
t.Join()
Else
t.Start()
End If
'Some code here
While t.IsAlive
System.Threading.Thread.Sleep("500")
End While
MessageBox.Show("All Files Completed")

Events being raised multiple times

I'm trying to create a class that will create a Relogin after certain time but after the first Relogin it keeps populating. Heres my Code:
Private Shared timer As Timer
Public Shared Event DoSomething As Action(Of Integer)
Private Shared _timesCalled As Integer = 0
Public Shared Sub Start()
AddHandler DoSomething, AddressOf EventHandler
timer = New System.Threading.Timer(AddressOf timer_Task, Nothing, 0, 1000)
End Sub
Public Shared Sub [Stop]()
timer.Dispose()
End Sub
Private Shared Sub timer_Task(State As Object)
_timesCalled += 1
If _timesCalled = 15 Then 'Should Raise event every 15s
RaiseEvent DoSomething(_timesCalled)
End If
End Sub
Private Shared Sub EventHandler(ByVal EventNumber As Integer)
My.Application.Dispatcher.Invoke(New Action(AddressOf OpenLogin))
End Sub
Private Shared Sub OpenLogin() 'This event fires multiple times after the first Event
Dim x As New MainWindow
x.ShowDialog() 'Dialog stops code from continuing.
x = Nothing
_timesCalled = 0
End Sub
Open_Login() fires multiple times after the first or second time. Doesn't seem to cause the same problem when I replace "MainWindow" object with a messagebox. Please Help. Thank you.
Notwithstanding the fact that your issue seems to be solved - using an unsynchronised counter is not a reliable way to have an event fired every predetermined period.
The timer event itself fires from a separate .NET managed thread and subsequently, the _timesCalled variable can be accessed from multiple threads. So it is possible that while you are re-setting _timesCalled=0 from your main thread another thread from the default threadpool is about to overwrite this with _timesCalled=14.
In your specific example it is simpler and more straightforward to reschedule the timer event after you’ve finished handling one. That way you can also account for the time it took you to process the event and the timer inaccuracies and lag.
Public Shared Sub Start()
...
' assuming this runs only once
timer = New System.Threading.Timer(AddressOf timer_Task, Nothing, 15000, Timeout.Infinite)
End Sub
Private Shared Sub timer_Task(State As Object)
RaiseEvent DoSomething(_timesCalled)
End Sub
Private Shared Sub OpenLogin()
Dim x As New MainWindow
x.ShowDialog()
x = Nothing
' Reschedule the timer again here, adjust the 15000 if necessary, maybe prefer timer.ChangeTime(...) instead
End Sub
Figured out it was my coding. Everytime MainWindow would load it would run Start() creating a new instance of Timer. Correct issue. Thanks for viewing

VB.Net Updating UI from background thread

I’m working on a scheduler like project using VB.Net, the application start from “Sub Main” with Application.Run(), all program codes are handler in a class, which is created and started here,
Public Sub Main()
m_App = New myApp
m_App.Start()
Application.Run()
End Sub
Inside the myApp, there has a timer to control the execution of task, and it will start a thread for each task, when the task complete, we try to display the alert window if there has error detected. We have tested two different way for communization between the execute thread and the main thread in order to display the alert window (frmAlert):
1) by adding pulic event in task object, then addhandler to the function in main thread
2) use delegate to notify the main thread
However, the alert window cannot be shown and there has no error reported. After debuging with the IDE, it was found that the alert windows has successful displayed, but it will closed when the task thread is completed.
Here is a simplified task class (testing with two communization methods),
Public Class myProcess
Public Event NotifyEvent()
Public Delegate Sub NotifyDelegate()
Private m_NotifyDelegate As NotifyDelegate
Public Sub SetNotify(ByVal NotifyDelegate As NotifyDelegate)
m_NotifyDelegate = NotifyDelegate
End Sub
Public Sub Execute()
System.Threading.Thread.Sleep(2000)
RaiseEvent NotifyEvent()
If m_NotifyDelegate IsNot Nothing Then m_NotifyDelegate()
End Sub
End Class
And the main application class
Imports System.Threading
Public Class myApp
Private WithEvents _Timer As New Windows.Forms.Timer
Private m_Process As New myProcess
Public Sub Start()
AddHandler m_Process.NotifyEvent, AddressOf Me.NotifyEvent
m_Process.SetNotify(AddressOf NotifyDelegate)
ProcessTasks()
End Sub
Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _Timer.Tick
ProcessTasks()
End Sub
Public Sub ProcessTasks()
_Timer.Enabled = False
'
Dim m_Thread = New Thread(AddressOf m_Process.Execute)
m_Thread.Start()
'
_Timer.Interval = 30000
_Timer.Enabled = True
End Sub
Public Sub NotifyEvent()
frmAlert.Show()
End Sub
Public Sub NotifyDelegate()
frmAlert.Show()
End Sub
End Class
It was found that the frmAlert is shown by using either NotifyEvent or NotifyDelegate, but it is closed immediately when the Execute is finished.
May I know how we can popup an alert window from the execution thread which can stay in the screen until user close it?
Thanks in advance!
You need to ensure that your main thread does not terminate if you want it to do anything when a sub-thread raises and event.
Public Sub Start()
AddHandler m_Process.NotifyEvent, AddressOf Me.NotifyEvent
m_Process.SetNotify(AddressOf NotifyDelegate)
ProcessTasks()
Do While True 'You might want to add a boolean condition here to instruct the main program to terminate when you want it to.
System.Threading.Thread.Sleep(200)
Loop
End Sub
This will prevent the main thread (program) to end, and thus will be available to handle any events raised by the sub-threads. Note: Your class lacks a termination condition.