VB.NET 2010, .NET 4
Hello all,
I have a System.Timers.Timer object that does some work on its elapsed event:
Private Sub MasterTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MasterTimer.Elapsed
MasterTimer.Enabled = False
'...work...
MasterTimer.Enabled = True
End Sub
My problem is that the work that it's doing sometimes gets stuck. Part of the work is serial communication so it might be getting stuck waiting for a response from something. I've already modified my serial communication code a bit to hopefully solve the problem. However, this timer is basically the heartbeat of a production control application and it is very bad if it were to stop for any reason. I was thinking that it might be nice to put in a fail-safe timeout so that, if the "work" is taking too long, the timer could re-enable itself and try again. I was thinking of something like this:
Move the work into a subroutine and create a delegate:
Private Delegate Sub WorkDelegate()
Private Sub Work()
'...work...
End Sub
Call the work by invoking the delegate and then use WaitOne(timeout) on the IAsyncResult to specify a timeout:
Private Sub MasterTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MasterTimer.Elapsed
MasterTimer.Enabled = False
Dim workDel as New WorkDelegate(AddressOf Work)
Dim result as IAsyncResult = workDel.BeginInvoke
result.AsyncWaitHandle.WaitOne(CInt(MasterTimer.Interval))
MasterTimer.Enabled = True
End Sub
But, my question is: Would this cause a problem if Work() really was stuck somewhere? In that it would re-enter a subroutine that is already running? Is there a way to abort Work() if it hasn't finished after the timeout? In other words, just cease execution of Work() if result.IsCompleted Is False after WaitOne?
I don't really understand this stuff very well so, any comments would be greatly appreciated. Perhaps there's an entirely different way to approach this that I don't know about?
Thanks a lot in advance!
I would like to add something:
Although I intend to do some rewrites as per Hans' suggestions, I had already scheduled a day of testing to try to isolate the source of this error. So far, it has only occurred when running the compiled application. I have spent today (and some yesterday) trying to reproduce the freeze while running in debug mode so that maybe I could add some breakpoints and figure out what's going on. So far, the program hasn't frozen in debug mode. I'm just wondering if there's anything different about the debug environment that might explain this. It might just be that I'm being "lucky", but I've run it probably three times the average time length after which the program froze when running the executable... Again, I'm pretty ignorant, but is there anything unique about the debug environment that could explain this? I'll update again if it freezes.
This goes wrong on the very first line of the code. The System.Timers.Timer class is pretty icky, there's absolutely no guarantee that call Stop() will prevent another call. The timer uses ThreadPool.QueueUserWorkItem to make the Elapsed event handler call. If the threadpool is busy this may end up queuing several calls, waiting to get the go-ahead from the TP scheduler to start running. Stopping the timer doesn't prevent those waiting threads from running. Without using a lock, those threads will step on each other badly and mess up your communication state.
A safe one is System.Threading.Timer with a period of zero so you'll get only one callback. Recharge the timer with its Change() method.
Calling a delegate's BeginInvoke() method and then blocking on it completing doesn't make sense. Just call Invoke(). Saves you from burning up another thread.
Yes, if the 'Work' method never returns then you have a problem. An unsolvable one.
A lot of this misery could disappear if you avoid using polling to see if there's anything available on the serial port. Let it tell you that there's something worthwhile going on. It raises the DataReceived event, on a threadpool thread, whenever there's at least one byte in the receive buffer. Using its WriteTimeout property is also an excellent way to avoid getting stuck when something is amiss with the communication protocol or the device. Dedicating one Thread and making blocking Read calls works well too.
Related
I cannot implement workerthreads since there are already too many classes developed with their own threads which call upon methods. It seems that, if there is any looping code to monitor completion of running threads, the only way to allow the started threads to complete their work is to feed them "sleep" time. Otherwise, sitting on a WaitOne outside of a thread or attempting anything using a Do-While loop to wait for threads to complete is difficult to successfully perform.
Below is my code which monitors completion of threads in a ThreadList, which works most of the time, however, I traced through breakpoints in a running Method (which was fired via a thread in an instantiated class), and the code simply determined that the thread completed, so execution left the method and continued in the external thread completion-monitoring code.
Do you see any problems in the code below which would cause a thread to falsely signal it was completed, causing execution to leave the For loop below? Also, will an overabundance of thread sleep time be added to memory using this approach?
startagain:
For Each t In threadList
If t.ThreadState = Threading.ThreadState.Stopped = False Then
wait(1)
GoTo startagain
End If
If t.ThreadState = Threading.ThreadState.Stopped = True Then Exit For
Next
Private Sub wait(ByVal seconds As Integer)
For i As Integer = 0 To seconds * 100
System.Threading.Thread.Sleep(10)
Application.DoEvents()
Next
End Sub
Visual Studio's debugger will switch from thread to thread if the breakpoints are there since they run concurrently. I believe that explains this part of your question "so execution left the method and continued in the external thread completion-monitoring code".
I speculate your problem is that you exit the for loop if a single thread is stopped, yet you are iterating through a list of threads. REMOVE this code and I believe your issue will be resolved:
If t.ThreadState = Threading.ThreadState.Stopped = True Then Exit For
Finally, here is a good MSDN link that describes different multithreading debugging techniques using Visual Studio.
I am trying to put some information in a database when windows begins to shutdown. In my application I am handling the Form.Closing event. However, Windows will go ahead and shut down and my Method doesn't have time to complete. Is there any way to pause the shutdown long enough to handle the shutdown? Here is my method that I am currently using.
Private Sub frmMain_Closing(sender As Object, e As FormClosingEventArgs) Handles Me.Closing
If e.CloseReason = CloseReason.WindowsShutDown Then
_logger.Debug("Hit frmMain_Closing1")
NewEvent(Events.SystemShutdownNormal)
System.Threading.Thread.Sleep(10000)
_logger.Debug("Hit frmMain_Closing 2")
End If
End Sub
Every time I am logging twice to a log file using the _logger.Debug("Hit...") However, only the first time I call it is getting written to the file. I have tried the method of setting e.Cancel = True, but that didn't seem to work.
Sorry for this misformation.
In short, there is no way to delay or cancel a Windows shutdown. It will tap all processes "nicely" to exit, then go through and kill any ones that haven't.
You can delay shutdown until your process finishes. To do this, you need to use ShutdownBlockReasonCreate().
You should be hooking the WM_QUERYENDSESSION message in order to get the opportunity to do something. You'll need to override the WinProc, deal with the message, and then call the base WinProc. Take a look at https://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents.sessionending(v=vs.110).aspx (scroll down to remarks.)
Keep in mind that Windows won't wait forever, but this will give you a bit of advance notification.. hopefully enough that you have a chance to do whatever you need to do. Prioritize the important stuff first, in the hopes that it finishes before the process is killed.
Once you've hooked WM_QUERYENDSESSION, you should call
ShutdownBlockReasonCreate() to block shutdown until your app is finished closing. Once finished, call ShutdownBlockReasonDestroy(). For more information, see this MSDN article (scroll down to "Applications that must block shutdown should use the new shutdown reason API").
The PInvoke declares can be found here and here.
I want to implement a timeout in my UDP Multicast receiver using VB.Net. Basically, if I don't receive any packets in 10 seconds I want to stop listening. I can very easily use a Timer with an interval of 10000 to know when I need to time out, but the question is how do I stop the receive function? If I use the Receive() function (the one that blocks), I could simply stop it with a Thread.Abort() call. Everything I have read, however, has said that this is not a safe practice. If I use the asynchronous BeginReceive() function, I don't know how to terminate it before it finishes normally because EndReceive() will throw an exception if it isn't called with an IASyncResult that is returned from BeginReceive().
The answers to this question led me to investigate the CancelAsync() method. But, the answer to this question made me nervous.
If I use the blocking receive, I will not be able to continuously poll the CancellationPending property unless I call Receive() in its own thread from within the DoWork handler. But that would mean it would continue to run after the cancel takes effect right? If I use BeginReceive(), I am worried that CancelAsync() wil get "eaten" by the DoWork handler and I will end up with the same problem.
Plus, this snippet from the BackgroundWorker documentation is less than reassuring...
Be aware that your code in the DoWork event handler may finish its work as a cancellation request is being made, and your polling loop may miss CancellationPending being set to true. In this case, the Cancelled flag of System.ComponentModel.RunWorkerCompletedEventArgs in your RunWorkerCompleted event handler will not be set to true, even though a cancellation request was made.
One alternative I thought of was having the UdpClient that is sending the packets be in charge of timing out, and then have it send some kind of cancellation signal packet to indicate that the receiver(s) should stop listening. The problem with this is that given the nature of UDP, there is no guarantee that said packet will arrive, or be picked up in the correct order.
Is there a way to safely terminate a UDP receive procedure before it finishes?
I have ran into the same issue with UdpClient and I am not sure what the safe solution is/if a "safe" solution exists. However, I came across a function that a user posted for a different question which tracks and terminates a code block that exceeds a certain time span and I just wrap my call to UdpClient.receive() in it. If you would like to give it a try, the function looks like this:
private static bool TrackFunction(TimeSpan timeSpan, Action codeBlock)
{
try
{
Task task = Task.Factory.StartNew(() => codeBlock());
task.Wait(timeSpan);
return task.IsCompleted;
}
catch (AggregateException ae)
{
throw ae.InnerExceptions[0];
}
}
And you would simply wrap it around your code like such:
bool timeTracker = TrackFunction(TimeSpan.FromSeconds(10), () =>
{
Byte[] received = myUdpClient.Receive(ref myIPEndPoint);
}
Again, there may be a better solution, but this is just what I have used.
I've run in to a similar situation where I open several connections (Udp, Serial, etc.) with remote devices and need to switch among them in a "listener" thread that uses the blocking UdpClient.Receive() call. Calling Thread.Abort() caused crashes, switching the connection instance (the UdpClient) without first exiting the thread didn't work either, because the thread was hung on the UdpClient.Receive() and a simple flag in a while loop never exited.
What did finally work was to close the connection in the main application thread, this would cause UdpClient.Receive() to throw an exception that could be caught and dealt with. The application creates instances of UdpClient that represent the various connections and starts a "listener" thread that can be terminated by setting a global flag and closing the current UdpClient instance. I used VB.NET and it looked something like this:
Dim mListening as Boolean 'application flag for exiting Listener thread
Dim mReceiver as UdpClient 'a connection instance
...
Private Sub Listener()
While mListening
Try
Dim reply = mReceiver.Receive()
Catch ex As Exception
'execution goes here when mReceiver.Close() called
End Try
End While
End Sub
The app sets mListening and starts the Listener thread.
When the app needs to "unblock" mReceiver, it calls mReceiver.Close() and handles it accordingly. I've used this scheme without any problems. I'm using VS 2019 and .NET v4.7
I'm developing a tiny UDP console to send some data to test some GPRS devices so I modify an example that I found in CodeProject that it uses one thread; but I get an issue when I want to exit the application, the treahd refuses to stop even if I do something like
If UdpOpen Then
ThreadReceive.Abort()
Me.Dispose()
UdpOpen = False
End If
It halt on the first code line
Private Sub UdpReceive()
Dim receiveBytes As [Byte]() = receivingUdpClient.Receive(RemoteIpEndPoint) '<--Halt here
IpRemote(RemoteIpEndPoint.Address.ToString)
Dim BitDet As BitArray
BitDet = New BitArray(receiveBytes)
Dim strReturnData As String = System.Text.Encoding.ASCII.GetString(receiveBytes)
If UdpOpen Then
StartUdpReceiveThread(CInt(RemotePortLbl.Text))
End If
PrintLog(strReturnData)
End Sub
So I do some research and found, usually, in this Web page the solution
Stop a thread that prevents program to close?
And, as says on the first comment, I turn to True the isBackground property and it work, now the question is Why?
Does somebody knows more in deep how it works?
A thread is only abortable when the CLR knows that it is safe to do so. Which will not be the case when the thread is buried deep inside an operating system call. That is fundamentally unsafe since the CLR cannot know if the thread has acquired any internal operating system locks that need to be released again.
By setting the IsBackground property to true, you tell the CLR that it is okay for the thread to not be aborted but leave it up to the operating system to clean-up anything that needs to released. Any taken locks cannot cause any problems anymore since there is no way for any code to run anymore that could deadlock. The operating system takes care of releasing the OS resources used by the socket. Much as it does if your program aborts for any other reason, like you terminating it with Task Manager.
The proper way to do this is to just call the socket's Dispose() method. That pulls the floor mat on the operating system call, it will stop waiting for anything to be received because the socket is a dead parrot. The Receive() call will complete with an ObjectDisposedException, be prepared to catch it.
Another very common scenario is to not use a thread at all but to use BeginReceive(). Cleanup works much the same way, when you call Dispose() then the callback will run. When you call EndReceive() then you get the ObjectDisposedException. Same way, be prepared to catch it and get out quick without doing anything else with the socket.
Using Thread.IsBackground is otherwise a pretty reasonable way to deal with shutdown, assuming you don't have to do anything intricate to tell the program on the other end of the wire that you stop listening for messages.
.NET has built-in support for receiving UDP asynchronously. See this:
UdpClient.BeginReceive
So you don't need to use threads in this case.
I am running code in a thread upon opening an Excel workbook that takes some time to execute. If I attempt to close the workbook while the thread is still executing, giving me the standard prompt to Save or cancel, my thread is interrupted (not sure if "interrupted" is technically the right term) when I click Cancel. More generally, it seems that UI interactions/updates cause the thread to be interrupted.
Is there a way to either 1) prevent the thread from being halted by a UI update/user interaction, or 2) allow the thread to resume after being interrupted?
Nothing fancy about the code:
Private Shared Sub Test()
Dim t As New Thread(AddressOf DoSomethingThatTakesAWhile)
t.Start()
End Sub
I made a bunch of progress on this, but ultimately could not get this to work using my original approach. So, I switched to .Net Framework 4.0 and used Tasks (System.Threading.Tasks namespace) to handle the job. Tasks worked beautifully and intuitively, and removed much of the complexity around thread management.