Activating timer from another thread - vb.net

So the purpose of it is to check the connection of the ftp server and if the ftp is up then enable timer1. I've read that threads don't work as synchonized and that is causing the problem. Without the thread it works fine, but the program hangs and stops responding constantly.
How can i activate a timer from another thread?
Maybe invoking and delegating would work? But i don't know how to do that.
Public Function CanPortOpen(ByVal HostName As String, ByVal Port As Integer) As Boolean
Dim TCP As New System.Net.Sockets.TcpClient
Try
TCP.Connect(HostName, Port)
Catch
End Try
If TCP.Connected = True Then
CanPortOpen = True
TCP.Close()
Timer1.Enabled = True
Else
CanPortOpen = False
TCP.Close()
Timer1.Enabled = False
FTPup.Abort()
End If
End Function
Public Sub CheckConnection()
CanPortOpen("HostName", Port)
End Sub
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
TestFTP = New System.Threading.Thread(AddressOf CheckConnection)
TestFTP.IsBackground = True
TestFTP.Priority = Threading.ThreadPriority.AboveNormal
TestFTP.Start()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
FTPup = New System.Threading.Thread(AddressOf UploadToFTP)
FTPup.IsBackground = True
FTPup.Priority = Threading.ThreadPriority.AboveNormal
FTPup.Start()
End Sub

I think before you start getting in too deep with threads you should start by looking at the BackgroundWorker component. You can find it on your toolbar in the designer and can drop it on your form. It gives you several events
DoWork - hook up this event with whatever you want done in a background thread
RunWorkerCompleted - hook up this event to run code in the main (UI) thread, triggered when the thread completes. Code here can interact with UI objects as normal.
There are other events that allow you to report progress to your main thread, etc. The purpose of the BackgroundWorker component is to make simple multithreading tasks like this easier.
Documentation is -> here
See -> here for examples of how to pass data from the worker thread to the main thread using EventArgs.
Alternatively, if you just want to run the timer from your thread you can do it like this :
'Declare an appropriate delegate
Delegate Sub dlgTimerEnable(ByVal enable as Boolean)
'the delegate should match the method signature
Private Sub TimerEnable(ByVal enable as Boolean)
Timer1.Enabled = enable
End Sub
and then in your thread procedure
Public Function CanPortOpen(ByVal HostName As String, ByVal Port As Integer) As Boolean
Dim TCP As New System.Net.Sockets.TcpClient
Try
TCP.Connect(HostName, Port)
Catch
End Try
If TCP.Connected = True Then
CanPortOpen = True
TCP.Close()
Me.Invoke(New dlgTimerEnable(AddressOf TimerEnable), New Object() {True})
Else
CanPortOpen = False
TCP.Close()
Me.Invoke(New dlgTimerEnable(AddressOf TimerEnable), New Object() {False})
FTPup.Abort()
End If
End Function
Here Invoke causes the method to be executed on the thread that owns Timer1 (assuming this is a method in your form where Me would refer to your form). The arguments are passed as an object.
You can even do this as a general way to work with any timer, for example :
Delegate Sub dlgTimerEnable(ByRef tmr As Timer, ByVal enable As Boolean)
Private Sub TimerEnable(ByRef tmr As Timer, ByVal enable As Boolean)
tmr.Enabled = enable
End Sub
and then :
Me.Invoke(New dlgTimerEnable(AddressOf TimerEnable), New Object() {Timer1, True})
This makes your delegate general - you can pass it any timer and enable/disable it.

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).

WinForms.IllegalCrossThreadCall with filewatcher

I'm new to Visual Basic and overall kind of new to coding in general.
Currently I work on a program which uses a filewatcher. But If I try this:
Public Class Form1
Private WithEvents fsw As IO.FileSystemWatcher
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
fsw = New IO.FileSystemWatcher("PATH")
fsw.EnableRaisingEvents = True
' fsw.Filter = "*.settings"
End Sub
Private Sub GetSettingsFromFile()
Some Code
More Code
CheckBox1.Checked = True
End Sub
Private Sub fsw_Changed(sender As Object, e As FileSystemEventArgs) Handles fsw.Changed
fsw.EnableRaisingEvents = False 'this is set because the file is changed many times in rapid succesion so I need to stop the Filewatcher from going of 200x (anyone has a better idea to do this?)
Threading.Thread.Sleep(100)
GetSettingsFromFile()
fsw.EnableRaisingEvents = True 'enabling it again
End Sub
End Class
But when I do this (trying to change anyhting in the form) I get this error:
System.InvalidOperationException (WinForms.IllegalCrossThreadCall)
It wont stop the program from working, but I want to understand what is wrong here and why the debugger is throwing this at me
regards
The event is being raised on a secondary thread. Any changes to the UI must be made on the UI thread. You need to marshal a method call to the UI thread and update the UI there. Lots of information around on how to do that. Here's an example:
Private Sub UpdateCheckBox1(checked As Boolean)
If CheckBox1.InvokeRequired Then
'We are on a secondary thread so marshal a method call to the UI thread.
CheckBox1.Invoke(New Action(Of Boolean)(AddressOf UpdateCheckBox1), checked)
Else
'We are on the UI thread so update the control.
CheckBox1.Checked = checked
End If
End Sub
Now you simply call that method wherever you are and whatever thread you're on. If you're already on the UI thread then the control will just be updated. If you're on a secondary thread then the method will invoke itself a second time, this time on the UI thread, and the control will be updated in that second invocation.

Properly closing a form running a loop in a new thread

I have method in my form that is running a do while loop in a new thread.
That for loop exits once a sentinel value is set to true (set by another method that handles Me.FormClosing)
The issue is that when the form closes, I get occasionally get two exceptions.
ObjectDisposedException and ComponentModel.Win32Exception.
How do I properly exit a form without "eating" these exceptions and ignoring them.
Code:
Dim _exit As Boolean
Public Sub test()
Dim task As Thread = New Thread(
Sub()
Do
checkInvoke(Sub() a.append("a"))
Loop While _exit = False
End Sub)
End Sub
Private Sub checkInvoke(ByVal _call As Action)
If Me.InvokeRequired Then
Me.Invoke(Sub() checkInvoke(_call))
Else
_call.Invoke()
End If
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
_exit = True
End Sub
Where does the error come from ?
This can be a bit confusing but it actually is pretty logical...
The user (or something else) closes the form.
FormClosing is then called which sets _exit to True
Then the Form closes itself, destroying its handle.
Now, it depends where it sometimes throws an exception :
Either the Thread just finished the Invoke or the Loop, check the _exit value then ends the loop, everything goes fine.
Either it just began the Invoke, then it calls a method invoking the UI thread to modify something on the form that has just been disposed, no more Handle to this form, leading to ObjectDisposedException
How to prevent this ?
One thing you can do is, in your FormClosing event, wait for the Thread to end, then letting the system close the form :
Private _isFinished As Boolean = False
Private _exit As Boolean = False
Public Sub test()
Dim task As Thread = New Thread(
Sub()
Do
checkInvoke(Sub() a.append("a"))
Loop While _exit = False
'We inform the UI thread we are done
_isFinished = True
End Sub)
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
_exit = True
While Not _isFinished
Application.DoEvent() 'We can't block the UI thread as it will be invoked by our second thread...
End While
End Sub
I am not familiar to VB, but I have done a similar thing in c++. The main problem is that the for loop does not finish yet when the form is closing. You can just hide the form, wait for the thread to finish and then close the form. You can use flags tomark the stopping of the paralel thread.

VB.NET : Thread is running or terminated; it cannot restart

I've encounter this error when using multi-threading. I'm new with it, in my windows application this code works. But I transfer it to windows services I've received "Thread is running or terminated; it cannot restart." I'm using System.Timers.Timer instead of System.Windows.Forms.Timer as recommended when creating it in Windows services. This windows services will export some XML file from database, so I need a timer. So time to time, it will check if there's a new products or customer in the database which reads the function below. By default, I've hard coded the time to 1min for testing. Also, I've created a boolean variable if the function is not finish yet. It will not override.
Here's my code :
Dim oIsproc_BP As Boolean
Dim oIsproc_ItemMaster1 As Boolean
Dim thrd As Thread
Protected Overrides Sub onstart(ByVal args() As String)
tmr.Interval = 1000
AddHandler tmr.Elapsed, AddressOf tmr_Elapsed
tmr.Start()
End Sub
Private Sub tmr_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles tmr.Elapsed
oIsproc_BP = False
oIsproc_ItemMaster1 = False
tSecItemMaster.Interval = 60000'oInterval(0)
AddHandler tSecItemMaster.Elapsed, AddressOf tSecItemMaster_Elapsed
tSecItemMaster.Start()
tSecCustomer.Interval = 60000'oInterval(2)
AddHandler tSecCustomer.Elapsed, AddressOf tSecCustomer_Elapsed
tSecCustomer.Start()
tmr.Stop()
End Sub
Private Sub tSecItemMaster_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles tSecItemMaster.Elapsed
If Not oIsproc_ItemMaster1 Then
oIsproc_ItemMaster1 = True
thrd = New Thread(DirectCast(Function() oItemMaster(), ThreadStart))
thrd.Start()
End If
Return
End Sub
Private Sub tSecCustomer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles tSecCustomer.Elapsed
If Not oIsproc_BP Then
oIsproc_BP = True
thrd = New Thread(DirectCast(Sub() oBPartners(, "C"), ThreadStart))
thrd.Start()
End If
Return
End Sub
And for my function :
Private Function oItemMaster(Optional ByVal FirstLoad As Boolean = False, Optional oType As Integer = 1)
''My code here
oIsproc_ItemMaster1 = False
End Function
Private Sub oBPartners(Optional ByVal FirstLoad As Boolean = False, Optional CardType As String = "C")
''My code here
oIsproc_BP = False
End Function
You have a race - you're using a single variable (thrd) to hold one of two instances of a Thread that you might create. Consider both timers firing at the same time, and the threads that service the timers being interleaved as follows:
Timer 1 (tSecItemMaster_Elapsed) Timer 2 (tSecCustomer_Elapsed)
If Not oIsproc_ItemMaster1 Then
oIsproc_ItemMaster1 = True
If Not oIsproc_BP Then
oIsproc_BP = True
thrd = New Thread(...)
thrd = New Thread(...)
thrd.Start()
End If
Return
thrd.Start()
End If
Return
And that's why you get the error message - both timers are trying to start the single Thread object that was created inside tSecItemMaster_Elapsed, and the Thread created inside of tSecCustomer_Elapsed is never started. Other inter-leavings will introduce similar issues.
A quick fix would be to create a separate field for storing each thread. I think you may still have a couple of race conditions but they're not leaping out at me at the moment.

Creating a form in a new thread (From an Event)

I have a small form that notifies the user on the completion of events (such as SMO restore).
I want this form to appear from various sources (such as the below SMO Restore complete event) so I guess I need to create a new thread before creating the form? As it could be called from outside the UI thread. (I also need to pass a string to this form)
The child form fades in out using a timer + Opacity.
What am I doing wrong here?
Private Sub CompleteEventHandler(ByVal sender As Object, ByVal e As Microsoft.SqlServer.Management.Common.ServerMessageEventArgs)
MyThread = New System.Threading.Thread(AddressOf DoStuff)
MyThread.Start("meh")
End Sub
Private Delegate Sub DoStuffDelegate(ByVal MsgString As String)
Private Sub DoStuff(ByVal MsgString As String)
If Me.InvokeRequired Then
Me.Invoke(New DoStuffDelegate(AddressOf DoStuff))
Else
Dim TempMessage As New frmNotification
TempMessage.lblMessage.Text = MsgString
TempMessage.Show()
End If
End Sub
Don't start a new thread, there's no point since you're already running on another thread and InvokeRequired will always be True. The mistake is that you call Me.Invoke() but forget to pass the "MsgString" argument. You'll also want to use Me.BeginInvoke(), no need to wait. Thus:
Private Sub CompleteEventHandler(ByVal sender As Object, ByVal e As EventArgs)
Me.BeginInvoke(New DoStuffDelegate(AddressOf DoStuff), "meh")
End Sub
Private Sub DoStuff(ByVal MsgString As String)
Dim TempMessage As New frmNotification
TempMessage.lblMessage.Text = MsgString
TempMessage.Show()
End Sub