How to let the code run smoothly using timers and different threads - vb.net

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)

Related

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.

VB.Net BeginGetResponse much faster than using Await GetResponseAsync?

I am trying to start using tasks, but I wanted to compare the speed difference when using a standard HttpWebRequest.BeginGetResponse.
From what I have found, it is taking ~600ms to send and complete 100 requests to example.com using BeginGetResponse
However, using Await GetResponseAsync is taking 5x that. Around 3000ms. In production, that really matters alot to me when scaled up. Am I doing something wrong, or is Await GetResponseAsync inherently slower than BeginGetResponse?
Imports System.Net
Public Class Form1
Private sw As New Stopwatch
Private respCounter As Integer
Private iterations As Integer = 100
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
sw.Start()
For i = 1 To iterations
Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
Dim state As New RequestState
state.req = req
req.BeginGetResponse(AddressOf respCallback, state)
Next
End Sub
Private Sub respCallback(ar As IAsyncResult)
Dim state As RequestState = ar.AsyncState
state.resp = state.req.EndGetResponse(ar)
state.respStream = state.resp.GetResponseStream
state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
End Sub
Private Sub readCallback(ar As IAsyncResult)
Dim state As RequestState = ar.AsyncState
Dim read As Integer = state.respStream.EndRead(ar)
If read > 0 Then
state.respBody += System.Text.ASCIIEncoding.ASCII.GetString(state.buffer, 0, read)
state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
Else
state.Dispose()
respCounter += 1
If respCounter = iterations Then
respCounter = 0
sw.Stop()
Debug.WriteLine(sw.ElapsedMilliseconds)
sw.Reset()
End If
End If
End Sub
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
sw.Start()
For i = 1 To iterations
Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
Using resp As WebResponse = Await req.GetResponseAsync
Using sr As New IO.StreamReader(resp.GetResponseStream)
Dim respBody As String = Await sr.ReadToEndAsync
End Using
End Using
respCounter += 1
If respCounter = iterations Then
respCounter = 0
sw.Stop()
Debug.WriteLine(sw.ElapsedMilliseconds)
sw.Reset()
End If
Next
MsgBox("Execution!")
End Sub
End Class
Public Class RequestState
Implements IDisposable
Public req As HttpWebRequest
Public resp As HttpWebResponse
Public respStream As IO.Stream
Public buffer(1024) As Byte
Public respBody As String
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
respStream.Close()
respStream.Dispose()
resp.Close()
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' ' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
' TODO: uncomment the following line if Finalize() is overridden above.
' GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
is Await GetResponseAsync inherently slower than BeginGetResponse?
It's difficult to address your specific performance concern without a good Minimal, Complete, and Verifiable code example. That said…
It seems to me that you're comparing apples and oranges here. First and foremost, there's a major difference in the implementations. In your BeginGetResponse() version, you initiate all of the requests concurrently, so assuming the web server will tolerate it, they complete in parallel. In your GetResponseAsync() version, you only initiate a new request after the previous one completes.
This serialization will necessarily slow everything down.
Beyond that, the BeginGetResponse() version performs all of its work in the IOCP thread pool, while the GetResponseAsync() version uses the single UI thread to handle completion of I/O events. The UI thread is a bottleneck, both because it can only do one thing at a time, and because you have to wait for it to be available from performing other tasks before it can move on to dealing with the I/O completions (a variation on the "can only do one thing at a time" issue).
In addition to that, you also have to deal with the latency involved in the message loop that dequeues the asynchronous completions for execution in the UI thread.
It wouldn't surprise me at all to find that the GetResponseAsync() approach is slower, when used in the way you're using it.
If you want better performance from it, you should probably use ConfigureAwait(false) in your async calls. Of course, this assumes you can otherwise minimize the interaction with the UI thread (e.g. the processing of the results does not actually need direct posting back to the UI thread). But doing so will tell the framework to not bother marshaling completions back to the UI thread. At least in the code you posted, this would be safe, as you don't actually interact with UI objects in the async event handler method.
All that said, when I changed your code so that it would run the GetResponseAsync() version concurrently, I found that at least with the web server I tested with, it worked just as fast as the BeginGetResponse() version. It was able to complete 100 iterations in just over 10 seconds in both cases.
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
sw.Start()
Dim tasks As List(Of Task(Of String)) = New List(Of Task(Of String))
For i = 1 To iterations
Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com/")
tasks.Add(ReadResponse(req))
Next
Await Task.WhenAll(tasks)
sw.Stop()
Debug.WriteLine(sw.ElapsedMilliseconds)
sw.Reset()
MsgBox("Execution!")
End Sub
Private Async Function ReadResponse(req As HttpWebRequest) As Task(Of String)
Using resp As WebResponse = Await req.GetResponseAsync
Using sr As New IO.StreamReader(resp.GetResponseStream)
Dim respBody As String = Await sr.ReadToEndAsync
Return respBody
End Using
End Using
End Function
It's possible with a faster web server, you might start to run into the UI-thread-as-a-bottleneck issue, but I would say the primary difference is likely just that the two implementations really aren't even logically the same.

Normal v Async calls to a service

I have a wcf service reference configured on a client application. It provides a whole series of functions to both retrieve and send data to a web based database. As an example:
Function errorCodesGetAll(ByVal uname As String, ByVal pword As String) As String
and
Function errorCodesGetAllAsync(ByVal uname As String, ByVal pword As String) As System.Threading.Tasks.Task(Of String)
I know that I can populate a rich text box with the first function by using the following code:
RichTextBox1.Text = getCountryList
Private Function getCountryList() As String
Dim svc As New ServiceReference2.ERSAPIServiceClient
svc.Open
Dim str As String = svc.errorCodesGetAll(username, password)
svc.Close()
Return str
End Function
As WCF is still a very new area to me I'm wondering how I would populate the same rich text box but this time using the Async variant of the errorCodesGetAll function?
Thanks for any advice or general pointers as to how the async variants are best used.
Your service will expose a "completed" event as well as the async method, you need to handle that event.
Open the service, wire up the event and call the async method
Private Sub GetCodes()
Dim svc As New ServiceReference2.ERSAPIServiceClient
AddHandler ServiceReference2.errorCodesGetAllCompleted, AddressOf errorCodesGetAllCompletedhandler
ServiceReference2.errorCodesGetAllAsync()
ServiceReference2.Close()
End Sub
Handle the event. This will get called when the service returns. (normally I would not add the "handler" to the end of the method and name it exactly the same as the event, but I thought it might help distinguish the event and the handler)
Private Sub errorCodesGetAllCompletedHandler(ByVal sender As Object, ByVal e As ServiceReference2.errorCodesGetAllEventArgs)
If Not e.Result Is Nothing Then
textbox.text = e.Result
End If
End Sub
Calling the async version of the method is interesting when you're on the UI thread (e.g., on the Click event handler for a button in your form), since that won't "freeze" the UI by blocking the thread, waiting for the networking call to complete.
Since you get the *Async method which returns a Task<T> result, I assume you're using the .NET Framework 4.5. If this is the case, you can take advantage of the Async / Await keywords to call the asynchronous version in a fairly simple way, while still preventing the UI thread from being blocked.
Private Async Sub Button_Click(ByVal sender as Object, ByVal e as EventArgs)
RichTextBox1.Text = Await getCountryList
End Sub
Private Async Function getCountryList() As Task(Of String)
Dim svc As New ServiceReference2.ERSAPIServiceClient
svc.Open
Dim str As String = Await svc.errorCodesGetAllAsync(username, password)
svc.Close()
Return str
End Function

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

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

Cross-thread communication and field-updating in VB.NET

I'm having some trouble getting cross-thread communication/field-updating working properly in my VB.NET 2010 program. I'm trying to update a field on my main form whenever a thread that I've started throws an event. Here's a simplified version of my code:
My main form:
Public Class Main
' stuff
' Eventually, startProcessing gets called:
Private Sub startProcessing()
Dim processingClass = New MyProcessingClass("whatever")
AddHandler processingClass.processStatusUpdate, AddressOf handleProcessStatusUpdate
Dim processingThread = New Thread(AddressOf processingClass.process)
processingThread.Start()
End Sub
Private Sub handleProcessStatusUpdate(statusUpdate As String)
txtMainFormTextBox.Text = statusUpdate ' InvalidOperationException
' "Cross-threaded operation not valid: Control 'txtMainFormTextBox' accessed from a thread other than the thread it was created on"
End Sub
End Class
The class which raises the event:
Public Class MyProcessingClass
Private whatever As String
Public Event processStatusUpdate(status As String)
Public Sub New(inWhatever As String)
whatever = inWhatever
End Sub
Public Sub process()
' do some stuff
RaiseEvent processStatusUpdate(whatever)
End Sub
End Class
As you can see, the handler in my main class doesn't have access to the TextBox I need since it was triggered by a different thread (I think). I've tried a number of other approaches to get this working, including:
Moving the event handler to MyProcessingClass, and passing txtMainFormTextBox by reference (ByRef) to the class.
Having the actual thread start inside of MyProcessingClass instead of Main.
None of these have worked. Clearly there's a concept that I'm missing here. What's the best way to get this done? Thanks!
You need to update the textbox on the UI thread by calling BeginInvoke.
You should use the BackgroundWorker component, which does all of this for you.
Simply handle the DoWork and ProgressChanged events.