VB.Net: timer and event handler influence each other - vb.net

In a Windows form I have:
a timer tmrBackup that regularly performs a backup of a local database
an event procedure that raises a procedure ImportNewFile when FileSystemWatcher detects a new file in a given directory.
Each of these work perfectly as long as they don't interfere each other.
I try to prevent that by stopping the FileSystemWatcher when a backup starts, and starting it again when it's finished.
On the other hand I set tmrBackup.Stop() in the beginning of ImportNewFile, then the contents of the file are transformed into a record in the local database. At the end of ImportNewFile I set tmrBackup.Start() again. But the timer is never started again, so that seems to be not a viable way.
When I don't set tmrBackup.Stop(), though, I see a strange behavior during debugging: when somewhere in ImportNewFile the timer fires, it seems that both routines are running parallel: one line in the tmrBackup routine is executed, then VB.Net jumps back to ImportNewFile for one line, then back to the timer, etc.
Question: what is a correct method to work with two objects with each being able to fire while the other's event is handled? Thanks!

Rather than attempting to temporarily disable the opposite timer, which is hokey and not guaranteed to work, the typical solution in such cases is to put SyncLock blocks around the necessary code in each of the event handlers, and have them both synchronize to the same object.
When code is surrounded by a SyncLock, execution will stop and wait, before entering the SyncLock block until any other related SyncLock code is finished. The way to relate them is by giving the SyncLock statement an object to synchronize against. If two SyncLock blocks synchronize to the same object, then neither of those two blocks of code will ever be allowed to run simultaneously.
In other words, if both blocks of code get called simultaneously, whichever starts first wins, and it runs through until it's complete, and the second just waits to begin executing until the first one is done.
Public Class SyncLockExample
Private WithEvents object1 As MyType1
Private WithEvents object2 As MyType2
Private syncObj As New Object()
Private Sub object1_MyEvent1(Object sender, EventArgs e) Handles object1.MyEvent1
SyncLock syncObject
' Do something that can't happen at the same time as object2_MyEvent2
End SyncLock
End Sub
Private Sub object2_MyEvent2(Object sender, EventArgs e) Handles object2.MyEvent2
SyncLock syncObject
' Do something that can't happen at the same time as object1_MyEvent1
End SyncLock
End Sub
End Class

Related

Label Text not updating even when using Refresh, Invalidate, and DoEvents

My code is designed to be a control system for a 2-axis motion system. I have 2 drives that each output a count of their steps. I can read the device, update a property, and update the text field of a label. However, it does not update the form. When I use a message box, I can display the text value being correct, but nothing updates the label.
I'm happy to try any suggestions, but I've been fighting this for about 16 hours and I'm at my wits end - as evidenced by the clear overkill/terrible coding that is shown in the code. I can't understand why it's not updating.
Additionally, a manual button with all versions seen below to refresh a form doesn't update the control.
Direction, recommendations?
Private Sub PositionChanged(ByVal sender As Object, ByVal e As EventArgs)
If TraverseController.InvokeRequired Then
TraverseController.Invoke(
New EventHandler(Of EventArgs)(AddressOf PositionChanged), sender, e)
Return
End If
'RaiseEvent PropertyChanged(TraverseController, New System.ComponentModel.PropertyChangedEventArgs("Position"))
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
TraverseController.lblLinearDrivePosDisp.Text = CStr(_position)
Application.DoEvents()
TraverseController.lblLinearDrivePosDisp.ResetBindings()
TraverseController.GBDrivePositionDisp.Refresh()
TraverseController.lblLinearDrivePosDisp.Refresh()
TraverseController.Refresh()
TraverseController.Invalidate()
TraverseController.Update()
Application.DoEvents()
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
End Sub
Assumption: TraverseController is form's class name.
This looks like a VB default form instance issue. It is apparent that you are trying to properly marshal control interaction back to the UI thread by using checking TraverseController.InvokeRequired. However, due to the way these default instance are created, TraverseController.InvokeRequired is creating a new instance of TraverseController on the secondary thread and all subsequent code is modifying that instance and not the one created on the UI thread.
One way to deal with this is to pass a synchronizing control instance to the class where PositionChanged changed method is defined and check that control's InvokeRequired method instead of TraverseController.InvokeRequired. If the containing class is itself a UI control, then use that class instance (Me.InvokeRequired).

Ensure a method only fires once when an event is triggered multiple times in Winforms newer method in .NET 4.5

So essentially I have wired up three text boxes to do a smart filter and want to let a user do a multi filter. The only problem was that it was firing too frequently and I want to have it fire after a delay. The event for 'TextChanged' is wired up to basically run and I have a simplified example of what I want:
I have a simple Winforms UI with two text boxes: "txtWait" and "txtTest". In the front end code the properties are default and the text are:
txtWait.Text = 1000
txtTest.Text = "Test Text I have here to look at"
A way to test this is to just hit the backspace a few times and wait. I would want only the last text to show once. I just got this part to work but the resetting it not occurring as I would expect. I would expect a person could hit backspace, backspace, (only a half a second had passed), backspace(clock resets and new wait begins).
And my code behind is:
Public Class DelayBeforeAction
Private _loaded As Boolean = False
Private _sw As Stopwatch = New Stopwatch()
Public Sub New()
InitializeComponent()
_loaded = True
End Sub
Private Sub txtTest_TextChanged(sender As Object, e As EventArgs) Handles txtTest.TextChanged
If _loaded Then
_sw.Start()
DelayExecute(Sub() If _sw.ElapsedMilliseconds > CInt(txtWait.Text) Then _sw.Reset() : MessageBox.Show(txtTest.Text) Else _sw.Reset(), CInt(txtWait.Text))
End If
End Sub
Private Async Sub DelayExecute(action As Action, timeout As Integer)
Await Task.Delay(timeout)
action()
End Sub
End Class
Concretely in your case, your first txtTest_TextChanged starts a stopwatch. Your second txtTest_TextChanged calls _sw.Start() again, which has no effect on a running stopwatch:
Starting a Stopwatch that is already running does not change the timer state or reset the elapsed time properties.
When the first txtTest_TextChanged's continuation runs, the stopwatch's elapsed time is expected to be greater than a second: it was started more than a second ago, and since then, all that happened is that other attempts were made to start the same stopwatch. Nothing was reset.
That said, using a stopwatch here is inherently unreliable and I do not recommend continuing down this path. You cannot be sure exactly when your continuation runs.
Instead, do not measure whether your continuation should probably be cancelled, track whether it was cancelled.
The most direct way in your particular case would be to increment a counter in txtTest_TextChanged. If the counter has not been changed by the time the continuation is executed, you know txtTest_TextChanged hasn't been called a second time.
A more general way is to use the CancellationTokenSource class. Most task-based methods, including Task.Delay, have overloads accepting CancellationToken instances. You can indicate a request for cancellation through CancellationTokenSource.Cancel.
Although you do not need it in this case, in general, you can also call CancellationToken.ThrowIfCancellationRequested explicitly in specific locations during long-running operations that would not otherwise be aborted.

How Can I RaiseEvent Manually For A FileSystemWatcher

I have extended on the FileSystemWatcher class to incorporate a FolderCount monitor and FolderEmpty monitor that raise events if a folder reaches a specified amount of files or if a folder returns to an empty status. I seem to have this working and I'm getting events raised when these conditions occur.
However, my problem is that when my FileSystemWatcher first initializes, it automatically goes in to check the folder contents of the specified folder to get a file count. If the limit is already reached, I need to raise an event immediately rather than wait for the FileSystemWatcher to report it.
Currently I can only seem to raise events by plugging into the .Created and .Deleted calls, however, because no files are getting created or deleted, I don't know how to raise my event manually.
Public Sub Initialize()
SetFolderCountStatus() 'Set the isFolderEmpty flag based on file contents
If Not isFolderEmpty Then
If options.WatchForFolderCount Then
If FileCountReached(options.FileCountToWatch) Then
RaiseEvent EventFolderCount(sender, e) 'Sender and e are never defined
End If
End If
End If
End Sub
My problem is that both sender and e are never populated with anything because they sit outside of my WatcherEventArgs.
I'm sure this can be done a better way, but I am unsure. Any help would be appreciated. Thanks
Do you actually use the sender and EventArgs in your EventFolderCount method? You can pass Me for the sender and an empty EventArgs object.
However What are the event arguments “sender” and “e” suggests attempting to raise the event isn't preferred. Instead you should have a single method that accomplishes the task and have that called in both places.
I actually resolved this by changing my EventHandler to only require a String variable, rather than EventArgs:
Public Event EventFolderCount(filename As String)
This way I could call it easily inside and outside of the FileSystemWatcher like so:
RaiseEvent EventFolderCount(filename)
Thanks #Dave Anderson for pointing me in the right direction.

What should I SyncLock in this code, and where?

I have a class that has two method in it, one calls a class which creates and executes a number of threads, the other is an event handler that handles an event raised when those threads complete (and then calls the first method again).
I understand that the method that handles the event runs in the thread that raised the event. So as such, I SyncLock a member variable that says how many threads are running and subtract one from it:
SyncLock Me ' GetType(me)
_availableThreads -= 1
End SyncLock
So I have a few questions:
Main Question: Should I be SyncLock'ing _availableThreads everywhere in the class - i.e in the method that creates the threads (which adds 1 when a thread is created)
Side Questions related to this question:
I'd usually SyncLock the current instance, but I've seen code that SyncLocks the type instead, so what is the difference between sync locking Me (Current Instance) and GetType(Me)?
Would there be a performance difference between the two? and is there anything smaller I'd be able to lock for the above that doesn't affect anything else - perhaps a separate 'padlock' object created for the sole purpose of locking things within a class?
Note: The sole purpose of _availableThreads is to control how many threads can run at any given time and the threads process jobs that can take hours to run.
Code:
Public Class QManager
Private _maxThreadCount, _availableThreads As Integer
Public Sub New(ByVal maxThreadCount As Integer)
Me.MaximumThreadCount = maxThreadCount
End Sub
Public Sub WorkThroughQueue()
//get jobs from queue (priorities change, so call this every time)
Dim jobQ As Queue(Of QdJobInfo) = QueueDAO.GetJobList
//loop job queue while there are jobs and we have threads available
While jobQ.Count > 0 And _availableThreads <= _maxThreadCount
//create threads for each queued job
Dim queuedJob As New QdJob(jobQ.Dequeue)
AddHandler queuedJob.ThreadComplete, AddressOf QueuedJob_ThreadCompleted
_availableThreads += 1 //use a thread up (do we need a sync lock here?)***************************
queuedJob.Process() //go process the job
End While
//when we get here, don't do anything else - when a job completes it will call this method again
End Sub
Private Sub QueuedJob_ThreadCompleted(ByVal sender As QdJobInfo, ByVal args As EventArgs)
SyncLock Me //GetType(me)
_availableThreads -= 1
End SyncLock
//regardless of how the job ended, we want to carry on going through the rest of the jobs
WorkThroughQueue()
End Sub
#Region "Properties"
Public Property MaximumThreadCount() As Integer
Get
Return _maxThreadCount
End Get
Set(ByVal value As Integer)
If value > Environment.ProcessorCount * 2 Then
_maxThreadCount = value
Else
value = Environment.ProcessorCount
End If
LogFacade.LogInfo(_logger, "Maximum Thread Count set to " & _maxThreadCount)
End Set
End Property
#End Region
End Class
You shouldn't SyncLock the instance or the type. You always want to SyncLock on a variable that is fully within the control of the class, and neither of those are. You should declare a private New Object and use that for your SyncLock.
Private lockObject as New Object()
...
SyncLock lockObject
...
End SyncLock
Unfortunately, you need to do a few things differently here.
First off, I'd recommend avoiding SyncLock, and using Interlocked.Increment and Interlocked.Decrement to handle changing _availableThreads. This will provide thread safety for that variable without a lock.
That being said, you still will need a SyncLock around every access to your Queue - if it's being used from multiple threads. An alternative, if you're using .NET 4, would be to change over to using the new ConcurrentQueue(Of T) class instead of Queue. If you use SyncLock, you should create a private object only accessible by your class, and use it for all synchronization.
You should be using the Interlocked class here, the Decrement() method to decrease the count. Yes, everywhere the variable is accessed.
Using SyncLock Me is as bad as SyncLock GetType(Me). You should always use a private object to lock on so nobody can accidentally cause a deadlock. The golden rule is that you cannot lock data, you can only block code from accessing data. Since the code is your private implementation detail, the object that holds the lock state must also be a private detail. Neither your object (Me) nor the Type of that object is private. Allowing other code to lock it by accident.
You can substitute the thread counter with Semaphore. If you use Semaphore you do not need to exit from while loop and neither it is necessary to call WorkThroughQueue() from ThreadCompleted event handler. Semaphore is thread safe so you can use it without locking.
http://www.albahari.com/threading/part2.aspx#_Semaphore

Event handler not removing itself?

At the beginning of a VB .NET function I remove an event handler and add it again at the end of the function because the function's code would trigger those events and I do not want them to trigger for the duration of the function. This usually works, but I have run into a few situations where the event still gets called even though I have already removed it. Sometimes removing it twice at the beginning of the function fixes it, but other times no matter how many times I remove it, it still fires. Any ideas on what could be causing this?
Edit
The code is in a Form that has a virtual mode datagridview. I want to run some operations that will trigger the CellValueNeeded event for the datagridview without that event being fired (because it will interfere).
Public Sub DoEventRaisingStuff()
RemoveHandler grid.CellValueNeeded, AddressOf grid_CellValueNeeded
'Do things that would trigger CellValueNeeded
AddHandler grid.CellValueNeeded, AddressOf grid_CellValueNeeded
End Sub
Removing the handler multiple times does not prevent the event from firing, so it doesn't seem to be added multiple times somewhere else by accident.
Is there a way to find out what event handlers are active?
If the event handling code is being called then one of two things is happening:
You aren't removing the event handler.
You are adding the event handler multiple times. This is the more usual case.
In the past the only way I've been able to spot 2. is to find all the places where the event handler is added (hopefully only one or two) and put break points on those lines. I've then run the application under the debugger and found that it breaks more times than I expect. I use the call stack to work out why - it's always me putting the add code in the wrong place (on a button press rather than on form instantiation for example).
You can do the same with the removal code. Count the number of times each break point is hit and if they're not the same work back up the call stack to see if you can work out why.
Use class scoped flag in the function and check the flag in the event handler.
i.e.:
Private RunFunction as Boolean = False
...
Private Sub MyEvent(e as system.eventargs) handles myObject.Method
If RunFunction Then
...
End If
End Sub
...
Private Sub MyFunction()
RunFunction = False
...
RunFunction = True
End Sub