When I start the timer during a process it freezes my program. Is there any way to resolve it? To make it not freeze all buttons in the GUI while the timer is working?
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Do somting...(I sending mail throught SMTP)
End Sub
This has nothing to do with the timer.
You're running a long (network-bound) operation on the UI thread.
Whenever code is running on the UI thread, the UI cannot respond.
You need to run that operation asynchronously or on a background thread.
Incase you still don't understand Slacks answer ...
instantiate the thread
Public t1 As Threading.Thread
make a call from your timer to the thread.
Private Sub someTimer(sender As Object, e As EventArgs) Handles someTimer.Tick
t1 = New Thread(New ThreadStart(AddressOf SomeSubRoutine))
t1.Start()
end sub
Run email code in the subroutine
sub Subroutine()
email code here // make sure that therer are no GUI or Main Thread calls else you have to get into delegates and invoke methods
end sub
your done, won't hang up the Gui Thread, however i do recoment staying away from timers I would call the thread directly
You can also try to use Application.DoEvents() inside your loop in Timer1_Tick function if you have a for-loop or while-loop inside the function.
Related
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.
Recently, I've been working with a system.form.timer in the UI thread. I've have noticed that while I can stop a timer on a background thread, I cannot start it back up unless I use BeginInvoke even though I do not receive a cross-threading exception. On a system.timers.timer however it seems that I can stop and start it from the background thread created by the timer. Why is this? Is a system.form.timer allowed to be stopped, but not enabled from a background thread? This seems a bit odd to me.
System.Form.Timer Code
DOESN'T WORK
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim BW As BackgroundWorker = New BackgroundWorker
AddHandler BW.DoWork, AddressOf CheckTimer
BW.RunWorkerAsync()
End Sub
Private Sub CheckTimer()
Timer1.Stop()
Timer1.Start()
MsgBox("Stopped and Started Timer")
End Sub
WORKS
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim BW As BackgroundWorker = New BackgroundWorker
AddHandler BW.DoWork, AddressOf CheckTimer
BW.RunWorkerAsync()
End Sub
Private Sub CheckTimer()
Timer1.Stop()
Me.BeginInvoke(New TimerStart(AddressOf TimerStartFunction))
MsgBox("Stopped and Started Timer")
End Sub
Private Delegate Sub TimerStart()
Private Sub TimerStartFunction()
Timer1.Start()
End Sub
System.Timers.Timer Code
WORKS
Dim aTimer As System.Timers.Timer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
aTimer = New System.Timers.Timer(5000)
AddHandler aTimer.Elapsed, AddressOf OnTimedEvent
aTimer.Enabled = True
End Sub
Sub OnTimedEvent()
aTimer.Stop()
aTimer.Start()
MsgBox("Stopped and Started Timer")
End Sub
The Winforms Timer class is somewhat thread-safe, not enough to keep you happy. When you call its Start() method then it creates a hidden window that turns WM_TIMER messages into Tick events.
When you do this on a worker thread then you tend to have a problem, these WM_TIMER messages are only dispatched when the thread runs a message loop. Worker threads don't normally do this, they don't call Application.Run(). So the timer just won't tick.
Calling the Stop() method is otherwise okay, it knows how to find that hidden window back even through the code runs on the wrong thread. The workaround you found with BeginInvoke() works because it now correctly calls Start() on the UI thread and gets the hidden window created with the correct owner thread, one that pumps. System.Timers.Timer doesn't have this problem, it doesn't rely on WM_TIMER to tick but uses a System.Threading.Timer instead. Which is supported by a dedicated worker thread managed by the CLR. Do note that this timer is pretty dangerous. Calling MsgBox() on a worker thread is fundamentally wrong, for one. High odds that the user never sees it since it will be behind a UI window.
That explains it, I can't otherwise offer better advice since I can't see what you are really trying to do. Be careful, you are playing with fire.
Researching I found out that Background Worker is a background thread, however when I run the following code Background Worker still runs until the end even when the main procedure is exited. Isn't this feature reserved to foreground threads?
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Run background worker
BackgroundWorker1.RunWorkerAsync()
'Display exit message
MsgBox("Main procedure exited")
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Wait 10 seconds
Threading.Thread.Sleep(10000)
'Modify some numbers
Dim variable = 3
variable -= 1
'Display exit message
MsgBox("Background thread " & variable & " exited")
End Sub
End Class
The Form1_Load method is not the "main procedure", so the message box that you're displaying at the end of it is actually a lie. That's just an event handler method for the Form.Load event, raised when your form is being displayed for the first time.
The "main procedure" is named Main and is defined in a separate file (actually, in VB.NET, it's automatically generated by the compiler and not even visible to you by default). You can find more information on the Main method in Microsoft's VB Programming Guide.
The Main method is still running as long as your program is still running. After the Form1_Load event handler method finishes, Form1 is still on the screen, so clearly your program hasn't closed yet. And since your program's main thread is still running, the BackgroundWorker object's background thread is still running, asynchronously, just like you told it to.
I have a class named TestClass
Public Class TestClass
Private _Count As Integer
Public ReadOnly Property Count() As Integer
Get
For i As Integer = 0 To 999999999
Threading.Thread.Sleep(100)
Next
Return 100
End Get
End Property
End Class
Form the main form, I call the class. Here is my code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim tg As New TestClass
MsgBox(tg.Count)
End Sub
When I call tg.Count from main form, the main form will become not responding. How to prevent my form from not responding. The user can access other menu rather than waiting for the result. Can anyone help me?
Use BackgroundWorker to run it in a worker thread. That will keep your GUI responding.
http://www.dreamincode.net/forums/topic/88605-the-background-worker/
Drag the BackgroundWorker componenet from the toolbox onto the form. Now, put your counter loop within the BackgroundWorker_DoWork event handler, which is autogenerated for you. All you need to do now is to call RunWorkerAsync on it.
Hope it helps!
Your loop is looping from 0 to 999999999 and delaying 100ms every loop which is halting that current thread. A better solution would be to use a timer, check for your completed condition and restart the timer if necessary. This way, your main form thread will always be responsive.
I have this situation: a Form with a System.Timer in it (with AutoReset = False). The form has its main thread and the timer its own thread too (nothing new here).
When the user press a button I need to stop the timer, wait until the timer thread has stopped its execution and do something more.
On the other side, the timer updates an item at the form so BeginInvoke is used. The code looks like this:
Button Code:
Private Sub ButtonStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonStop.Click
SyncLock (m_stopLock)
m_stopProcessTimer = True
Threading.Monitor.Wait(m_stopLock)
End SyncLock
''#Do more things here after the timer has end its execution and is stopped
End Sub
Timer code:
Private Sub m_processTimer_Elapsed(ByVal sender As Object, ByVal e As System.EventArgs) Handles m_processTimer.Elapsed
Dim auxDelegate As EventHandler
SyncLock (m_stopLock)
If Not m_stopProcessTimer Then
If Me.InvokeRequired Then
auxDelegate = New EventHandler(AddressOf m_processTimer_Elapsed)
Me.BeginInvoke(auxDelegate, New Object() {sender, e})
Else
DoFormStuf()
m_processTimer.Start()
End If
Else
Threading.Monitor.Pulse(m_stopLock)
End If
End SyncLock
End Sub
The point is that I wait the main thread to let the timer thread to end its work.
The problem is that this code deadlocks when the user clicks the button when the BeginInvoke is going to be called. How a simple thing like this one can be done? Looks like I cannot find a good solution to this problem :(
Don't use locks at all, just make sure to do everything on the UI thread, and you can guarantee that nothing will be corrupted. Remember that dispatcher items run on the UI thread, so you know that if you're doing everything either in a dispatcher item or an event handler, only one thing is executing at a time.
1) Perhaps a little more code would be helpful. Do you create a new thread and put the timer ON that thread?
2) Have you tried using ManualResetEvent (.WaitOne() and .Set() instead?)
3) In your event, if invoke is required, you are re-calling your event again. Confusing...
4) Are you supposed to wait until the other thread is done? Then Thread.Join()?