What happens if the callback method takes more than the specified Timer's time period? - vb.net

I have the below code. What happens if the Upload method takes more than one minute for completion? Does the timer kick off another call to the Upload method immediately after one minute is over or Does it wait until the method finishes executing? I am implementing this as a windows service.
Protected Overrides Sub OnStart(ByVal args() As String)
Dim tmr As Timer = New Timer(New TimerCallback(AddressOf Upload), Nothing, 1000, **60000**)
While Not tmr Is Nothing
End While
End Sub
Public Sub Upload(ByVal o As Object)
Dim sr As StreamReader
..........
..........
End Sub

Though some of your code is confusing (what's with the While loop?), I can say yes, the Timer will kick off another Thread from the ThreadPool.
You can use Monitor, ReaderWriterLockSlim, or lock to control re-entrancy. You'll need an object to lock onto and other things tie into your requirements (i.e. the integer specified tells Monitor to try for X milliseconds, where 0 means we want the lock immediately):
VB.NET:
Private ReadOnly LockerObject As Object = New Object()
If (Monitor.TryEnter(LockerObject, 0)) Then
Try
'do your processing
Catch
Finally
'always release the lock
Monitor.Exit(LockerObject)
End Try
End If
C#:
private readonly object LockerObject = new object();
if (Monitor.TryEnter(LockerObject, 0))
{
try
{
//do your processing
}
catch
{
}
finally
{
//always release the lock
Monitor.Exit(LockerObject);
}
}
else
{
//do something else here if needs be.
}
EDIT: Oops, this is VB.NET! Let me convert my code...

Related

How to let the code run smoothly using timers and different threads

I'm trying to prevent the GUI from freezing, because of a low timer interval and too much to process in the Timer.Tick event handler.
I've been googling a while and I understood that I cannot update UI from any other thread other than the UI thread.
So, what about if you are using lots of controls under Timer1.Tick?
How can I update a Label when the data is downloaded with WebClient with a timer, you don't want to lower the interval too much and keep the UI responsive at the same time?
I receiver Cross Thread violation exceptions when I access UI elements, a ListBox1 and a RichTextBox.
What is the correct way to update the UI with a timer and/or a Thread without causing cross threat exceptions?
You have different ways to update UI elements from a Thread other than the UI Thread.
You can use the InvokeRequired/Invoke() pattern (meh), call the asynchronous BeginInvoke() method, Post() to the SynchronizationContext, maybe mixed with an AsyncOperation + AsyncOperationManager (solid BackGroundWorker style), use an async callback etc.
There's also the Progress<T> class and its IProgress<T> interface.
This class provides a quite simplified way to capture the SynchronizationContext where the class object is created and Post() back to the captured execution context.
The Progress<T> delegate created in the UI Thread is called in that context. We just need to pass the Progress<T> delegate and handle the notifications we receive.
You're downloading and handling a string, so your Progress<T> object will be a Progress(Of String): so, it will return a string to you.
The Timer is replaced by a Task that executes your code and also delays its actions by a Interval that you can specify, as with a Timer, here using Task.Delay([Interval]) between each action. There's a StopWatch that measures the time a download actually takes and adjusts the Delay based on the Interval specified (it's not a precision thing, anyway).
▶ In the sample code, the download Task can be started and stopped using the StartDownload() and StopDownload() methods of a helper class.
The StopDownload() method is awaitable, it executes the cancellation of the current tasks and disposes of the disposable objects used.
▶ I've replaced WebClient with HttpClient, it's still quite simple to use, it provides async methods that support a CancellationToken (though a download in progress requires some time to cancel, but it's handled here).
▶ A Button click initializes and starts the timed downloads and another one stops it (but you can call the StopDownload() method when the Form closes, or, well, whenever you need to).
▶ The Progress<T> delegate is just a Lambda here: there's not much to do, just fill a ListBox and scroll a RichTextBox.
You can initialize the helper class object (it's named MyDownloader: of course you will pick another name, this one is ridiculous) and call its StartDownload() method, passing the Progress<T> object, the Uri and the Interval between each download.
Private downloader As MyDownloader = Nothing
Private Sub btnStartDownload_Click(sender As Object, e As EventArgs) Handles btnStartDownload.Click
Dim progress = New Progress(Of String)(
Sub(data)
' We're on the UI Thread here
ListBox1.Items.Clear()
ListBox1.Items.AddRange(Split(data, vbLf))
RichTextBox1.SelectionStart = RichTextBox1.TextLength
End Sub)
Dim url As Uri = New Uri("https://SomeAddress.com")
downloader = New MyDownloader()
' Download from url every 1 second and report back to the progress delegate
downloader.StartDownload(progress, url, 1)
Private Async Sub btnStopDownload_Click(sender As Object, e As EventArgs) Handles btnStopDownload.Click
Await downloader.StopDownload()
End Sub
The helper class:
Imports System.Diagnostics
Imports System.Net
Imports System.Net.Http
Imports System.Text.RegularExpressions
Public Class MyDownloader
Implements IDisposable
Private Shared client As New HttpClient()
Private cts As CancellationTokenSource = Nothing
Private interval As Integer = 0
Private disposed As Boolean
Public Sub StartDownload(progress As IProgress(Of String), url As Uri, intervalSeconds As Integer)
interval = intervalSeconds * 1000
Task.Run(Function() DownloadAsync(progress, url, cts.Token))
End Sub
Private Async Function DownloadAsync(progress As IProgress(Of String), url As Uri, token As CancellationToken) As Task
token.ThrowIfCancellationRequested()
Dim responseData As String = String.Empty
Dim pattern As String = "<(?:[^>=]|='[^']*'|=""[^""]*""|=[^'""][^\s>]*)*>"
Dim downloadTimeWatch As Stopwatch = New Stopwatch()
downloadTimeWatch.Start()
Do
Try
Using response = Await client.GetAsync(url, HttpCompletionOption.ResponseContentRead, token)
responseData = Await response.Content.ReadAsStringAsync()
responseData = WebUtility.HtmlDecode(Regex.Replace(responseData, pattern, ""))
End Using
progress.Report(responseData)
Dim delay = interval - CInt(downloadTimeWatch.ElapsedMilliseconds)
Await Task.Delay(If(delay <= 0, 10, delay), token)
downloadTimeWatch.Restart()
Catch tcEx As TaskCanceledException
' Don't care - catch a cancellation request
Debug.Print(tcEx.Message)
Catch wEx As WebException
' Internet connection failed? Internal server error? See what to do
Debug.Print(wEx.Message)
End Try
Loop
End Function
Public Async Function StopDownload() As Task
Try
cts.Cancel()
client?.CancelPendingRequests()
Await Task.Delay(interval)
Finally
client?.Dispose()
cts?.Dispose()
End Try
End Function
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposed AndAlso disposing Then
client?.Dispose()
client = Nothing
End If
disposed = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
End Sub
End Class
Your listbox and richtextbox accesses must run on the UI thread. The easiest way to do it is like this.
Me.Invoke(Sub()
ListBox1.Items.Clear()
ListBox1.Items.AddRange(Split(clientdecode, vbLf))
RichTextBox1.SelectionStart() = RichTextBox1.TextLength
RichTextBox1.ScrollToCaret()
End Sub)

Issue with updating the form using Async/Wait

In my application, I call a process to update software - which is stored within its own class. Even thou I have am Async/Wait and debug.print is returning a message within frmUpdate.ReportProgress() for some reason the progress bar in the update form is not updating...
Class
Namespace software
Public Class updater
Public Async Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Tasks.Task(Of Boolean)
Dim progressIndicator = New Progress(Of Integer)(AddressOf ReportProgress)
Await SetProgressBar(progressIndicator, 100)
Return True
End Function
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
myProgress.Report(counter)
End Function
Private Function ReportProgress(ByVal count As Integer)
frmUpdate.ReportProgress(count)
End Function
End Class
End Namespace
Code for the Form Below
Public Class frmUpdate
Private Async Sub btnUpdate_Click(sender As System.Object, e As System.EventArgs) Handles btnUpdate.Click
updater.UpdateSoftware(url, downloadFolder)
End Function
Public Function ReportProgress(ByVal myInt As Integer)
ProgressBar1.Value = myInt
Debug.Print(ProgressBar1.Value)
End Function
End Class
Async and Await do not inherently make things multi-threaded. There's good reason for that. Not all asynchronous tasks are multi-threaded. For instance, if you want to have some task wait until the user clicks a button, you don't need that task to be on it's own thread. That would be very wasteful to have a separate thread just sitting there looping while it waits for the button to be clicked. In a situation like that, you'd just have the click event of the button signal the task to continue/complete. Then, the task can simply block execution until that signal is received. Since it's the case that making all asynchronous tasks multi-threaded is wasteful, starting a task in a new thread is left as a separate optional action.
Simply adding the Async keyword to your method signature does nothing to make your method run on a separate thread. In fact, it technically doesn't even make your method asynchronous. It will only be asynchronous if you call Await inside the method somewhere. So, there is no appreciable difference between your method:
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
myProgress.Report(counter)
End Function
And this:
Private Sub SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer)
myProgress.Report(counter)
End Sub
Both methods execute immediately when they are called and both block execution in whatever method called them until they are complete. If myProgress, whatever that is, provides an asynchronous version of the Report method (i.e. an equivalent method that returns a Task), then you want to call that and await on it:
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
Await myProgress.ReportAsync(counter)
End Function
If no such asynchronous alternative exists, then you can force it to be asynchronous by starting it in its own thread:
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
Await Task.Run(Sub() myProgress.ReportAsync(counter))
End Function
However, in this case, I'm quite certain, given the name of the method, that it doesn't really need to be asynchronous at all. The real issue is that whatever the long-running work you're doing in UpdateSoftware is, it's not being done with Await, so it's what's blocking when it shouldn't be. But, since you didn't show that code, it's hard to give a good example. Consider something like this:
Public Class updater
Public Async Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Tasks.Task(Of Boolean)
Await Task.Run(AddressOf LongRunningWork)
Return True
End Function
Private Sub LongRunningWork()
' Do something that takes a while
For i As Integer = 1 to 100
ReportProgress(i)
Thread.Sleep(100)
Next
End Sub
Private Sub ReportProgress(count As Integer)
frmUpdate.BeginInvoke(Sub() frmUpdate.ReportProgress(count))
End Function
End Class
Note that in the ReportProgress method, I have it calling BeginInvoke (though Invoke would work too) on the form to get it to execute that form's method on the UI thread rather than on the task's thread. That's always important to do. Anytime you are updating the UI, you always need to invoke back the the UI thread to do the updating, otherwise you'll get cross-thread exceptions.
Also, you aren't using Await in the event handler either, which you ought to do. Technically, it works either way, but if you start adding exception handling, you'll find out quickly it makes a big difference:
Private Async Sub btnUpdate_Click(sender As System.Object, e As System.EventArgs) Handles btnUpdate.Click
Await updater.UpdateSoftware(url, downloadFolder)
End Function
For more information about Async/Await (Microsoft calls it the Task-based Asynchronous Pattern, or TAP for short), see the documentation. TAP is a really powerful tool. It makes asynchronous code very easy to read and understand. But, despite appearing simple on the surface, it still requires a good understanding of the underlying concepts to use it properly. If you are feeling uncertain about it, you may want to try using the BackgroundWorker component, as I suggested in my answer to your previous question, since it's a little less magical and may be easier for you to see what's happening where and why.

Cancelling a delayed task before it has completed

I am creating a list of tasks that are to be completed at different times of the day. For each task I create, I associate it with a CancellationTokenSource. However, calling PoolOfTasks(0).Token.Cancel() seems to just make the task execute instantly instead of not at all. I wish to completely abort the task from running.
If the task is not cancelled, it should execute after the specified delay of 10 seconds.
Here's the code I've cooked up so far - I'm confused as to why the method is still invoked (and why it is invoked without a delay) when I cancel the CancellationToken. I have probably misunderstood the concept of them.
Module Module1
Public Structure TaskCheckout
Public Token As CancellationTokenSource
Public Task As Task
Public TimeToCheckout As TimeSpan
End Structure
Private PoolOfTasks As New ObservableCollection(Of TaskCheckout)
Sub Main()
Dim Checkout As New TaskCheckout
With Checkout
.Token = New CancellationTokenSource
.TimeToCheckout = TimeSpan.FromSeconds(10)
.Task = Task.Delay(Checkout.TimeToCheckout, .Token.Token).ContinueWith(AddressOf methodToCall)
End With
PoolOfTasks.Add(Checkout)
Task.Delay(500)
PoolOfTasks(0).Token.Cancel()
Console.ReadKey()
End Sub
Private Sub methodToCall()
MsgBox("Hey!")
End Sub
End Module

Thread Safety in VB.Net

I'm trying to run a multi-threaded console app in VB and am having thread cross-over. Basically I want to run 5 threads, have them continually access a queue, process, and repeat until there's nothing left. When all threads have processed I want them to do something else. I'm attempting to use SyncLock to prevent multiple threads from accessing but it does not seem to be working. Any help would be appreciated!
Dim iThread As Integer
Dim manualEvents(4) As ManualResetEvent
Sub Main()
For i = 0 To 4
manualEvents(i) = New ManualResetEvent(False)
ThreadPool.QueueUserWorkItem(AddressOf DoOne)
Next
For Each handle As WaitHandle In manualEvents
handle.WaitOne()
Next
' do other stuff
EndSub
Private Sub DoOne()
Dim lockObject As New Object()
SyncLock (lockObject)
If QueryQueue.DoOne() Then
DoOne()
Else
manualEvents(iThread).Set()
iThread = iThread + 1
End If
End SyncLock
End Sub
The problem is with the locked resource, you're using lockObject as a synchronization lock resource which should be shared accros threads.
You have to make it an instance field.
Private Shared lockObject As New Object()
Private Sub DoOne()
SyncLock (lockObject)
If QueryQueue.DoOne() Then
DoOne()
Else
manualEvents(iThread).Set()
iThread = iThread + 1
End If
End SyncLock
End Sub
The problem is that you are creating and using a new instance of an object for locking on each thread. The naive solution is to promote lockObject from a local variable to class variable. That way each thread is using the same object to lock on. I say this is naive because you have exchanged one problem for another (albeit less severe). The new problem is that you have now made your parallel algorithm a serial algorithm since only one thread can being doing work at any given time.
The solution would be to lock access to the queue only while it is being changed. Then operate on the dequeued objects outside the lock so that the threads can perform work concurrently.
If .NET 4.0 is available to you could refactored the code like this.
Public Class Example
Private m_Queue As ConcurrentQueue(Of SomeObject) = New ConcurrentQueue(Of SomeObject)()
Public Shared Sub Main()
' Populate the queue here.
Dim finished = New CountdownEvent(1)
For i As Integer = 0 to 4
finsihed.AddCount()
ThreadPool.QueueUserWorkItem(AddressOf DoOne, finished)
Next
finished.Signal()
finished.Wait()
End Sub
Private Shared Sub DoOne(ByVal state As Object)
Try
Dim item As SomeObject = Nothing
Do While m_Queue.TryDequeue(item) Then
' Process the dequeued item here.
Loop
' There is nothing left so do something else now.
Finally
Dim finished = DirectCast(state, CountdownEvent)
finished.Signal()
End Try
End Sub
End Class
I used ConcurrentQueue to avoid having to use SyncLock entirely. I used CountdownEvent as a more scalable alternative to wait for work items to complete.
You need to share the same lockObject across the threads by making it an instance variable.

how to know when a work in a thread is complete?

I need to create multiple threads when a button is clicked and i've done that with this:
Dim myThread As New Threading.Thread(AddressOf getFile)
myThread.IsBackground = True
myThread.Start()
but i need to update a picture box with the downloaded file, buy if i set an event in the function getFile and raise it to notify that the files was downloaded and then update the picturebox.
Use an AsyncResult, and either check it periodically for completion, or provide a delegate to be called when the thread has completed its work.
A complete example in VB can be found here.
You need to make use of MethodInvoker deligate.
Public Sub GetFile()
If Me.InvokeRequired Then
Me.Invoke(New MethodInvoker(GetFile))
End If
End Sub
Now you can handle any event in your specified class.
You can achive that using the Asyncallback, ...
Dim sinctotal As New Del_sinc(AddressOf sincronizar)
Dim ar As IAsyncResult = sinctotal.BeginInvoke(_funcion, type, New AsyncCallback(AddressOf SincEnd), cookieobj)
The cookieobj is this
Class Cookie
Public id As String
Public AsyncDelegate As [Delegate]
Sub New(ByVal id As String, ByVal asyncDelegate As [Delegate])
Me.id = id
Me.AsyncDelegate = asyncDelegate
End Sub
End Class
When the delegate finish it will call the funcion Sincend (in this example), then you could use a event to update your picture.
Hope this helps!