Trying to get asynchronous functions with arguments and returns to work - vb.net

I have been trying to get a function to work asynchronously with a GUI window, and having very limited success.
The code below is my function, XXX, with a delegate and Callback - to allow stopping the GUI thread, without getting an error.
It works though - if it is a Sub, not a function, and if it doesn't have any arguments.
I don't know how to change it so I can check its return value... I have found a little help in some examples, http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncstate.aspx - but they print the return value in the callback, and I can't see how to get it in the caller.
I can't find any way to use arguments in my function.
Private Function XXX_Callback(ByVal ia As IAsyncResult)
Dim d As XXXDelegate = CType(CType(ia, Runtime.Remoting.Messaging.AsyncResult).AsyncDelegate, XXXDelegate)
d.EndInvoke(ia)
Dim result As AsyncResult = CType(ia, AsyncResult)
Dim caller As XXXDelegate = CType(result.AsyncDelegate, XXXDelegate)
Dim returnValue As Boolean = caller.EndInvoke(ia)
XXX_Finish() ' needs the "vvv argument, I don't know how to get it
' The returnValue is here but I don't know how to send it to the caller
End Function
'Private Function XXX_Finish(ByVal vvv as Boolean) As Boolean
' this probably needs to return something, I don't know what/ how to get it
Private Function XXX_Finish() As Boolean
' something
myGui.Finish()
End Function
' Private Delegate Function XXXDelegate(ByVal vvv As Integer) As Boolean
' Public Function XXX(ByVal vvv As Integer) As Boolean ' This is what I would like
Private Delegate Sub XXXDelegate()
Public Sub XXX()
'
myGui.Update()
'
End Sub
Public Sub Caller()
'
myGui = New SomeGui()
myGui.Begin()
Dim t As New XXXDelegate(AddressOf XXX)
t.BeginInvoke(AddressOf XXX_Callback, Nothing)
' more code, another call
End Sub
Private myGui As SomeGui
Please, could someone help me get this into a better shape, or get some examples that will help ? With the ones I have found in the past two days, I have reached a dead end...
Thank you.

After searching through lots of posts on the web, and reading a couple of books, I have found the best resource, with examples for each case that one might encounter:
http://support.microsoft.com/kb/315582
The answers to this particular question are in the above link, "Sample 2: Calling A Method Asynchronously by Using the EndInvoke() Call Pattern" and "Sample 5: Executing a Callback When an Asynchronous Method Completes."
The best thing about it though is the simple and organized fashion in which the options to use Invoke, EndInvoke, Callback are explained.
One thing to note: for my question above, the essential breakthrough was when I read the words "BeginInvoke() returns immediately and does not wait for the asynchronous call to complete."
So... trying to call another method, after it, was the wrong approach.

This is how i made an asyncronous function calls.
I declare the function in the module.
Private Delegate Function fnBolAsyncCallVerificacionNuevaVersion(ByVal pIntModo As Integer, ByVal pIntAccion As Integer) As Boolean
Private Delegate Function fnBolAsyncCallActualizacionTipoCambio(ByVal pIntActualizacionMandatoria As clsBusinessBoxData.tblTipoCambio.enumActualizacionMandatoria) As typBolResultadoFuncion
The functions receive some parameters and the first returns a boolean and the second returns an structure with 2 data, one boolean and one string.
In the load event of the form I call the functions.
sAsyncVerificaVersion = New fnBolAsyncCallVerificacionNuevaVersion(AddressOf fnBolVerificaActualizacion)
sAsyncVerificaVersion.BeginInvoke(enumDisplayGUIMode.Silent, typApplicationUpdate.CheckOnly, New AsyncCallback(AddressOf fnBolTerminaVerificacionVersion), Nothing)
sAsyncActualizaTiposCambio = New fnBolAsyncCallActualizacionTipoCambio(AddressOf fnBolActualizaTiposCambioYahoo)
sAsyncActualizaTiposCambio.BeginInvoke(clsBusinessBoxData.tblTipoCambio.enumActualizacionMandatoria.No, New AsyncCallback(AddressOf fnBolTerminaActualizacionTipoCambio), Nothing)
These calls will execute the function asyncronous and will callback the function defined when finished.
Then i receive the results in the callback functions defined.
Private Function fnBolTerminaVerificacionVersion(ByVal pIarResultado As IAsyncResult) As Boolean
Dim sClsResultado = CType(pIarResultado, Messaging.AsyncResult)
Dim sIarResultado As fnBolAsyncCallVerificacionNuevaVersion = CType(sClsResultado.AsyncDelegate, fnBolAsyncCallVerificacionNuevaVersion)
Dim sBolExisteNuevaVersion As Boolean = False
CheckForIllegalCrossThreadCalls = False
sBolExisteNuevaVersion = sIarResultado.EndInvoke(pIarResultado)
mnuBajarActualizacion.Enabled = sBolExisteNuevaVersion
CheckForIllegalCrossThreadCalls = True
Return True
End Function
Private Function fnBolTerminaActualizacionTipoCambio(ByVal pIarResultado As IAsyncResult) As Boolean
Dim sBolActualizacionExitosa As typBolResultadoFuncion
Dim sClsResultado = CType(pIarResultado, Messaging.AsyncResult)
Dim sIarResultado As fnBolAsyncCallActualizacionTipoCambio = CType(sClsResultado.AsyncDelegate, fnBolAsyncCallActualizacionTipoCambio)
CheckForIllegalCrossThreadCalls = False
sBolActualizacionExitosa = sIarResultado.EndInvoke(pIarResultado)
CheckForIllegalCrossThreadCalls = True
Return True
End Function
I hope they help.

Related

VB - CefSharp not return values

I can fill a value of a specific field with the code below
nav.EvaluateScriptAsync("document.getElementById('email').value='teste';")
but I can't return the value of this field
Dim placeholder As Task(Of JavascriptResponse) = nav.EvaluateScriptAsync("document.getElementById('email').value")
Task.WaitAll()
MsgBox(placeholder.Result.ToString)
both (placeholder.Result.ToString) and (placeholder.ToString) return "CefSharp.JavascriptResponse"
You can obtain the value from JavascriptResponse.Result property. Check the Success property first, if the script evaluation failed the error will be contained with the Message property.
http://cefsharp.github.io/api/97.1.x/html/Properties_T_CefSharp_JavascriptResponse.htm
I got it that way.
Public Async Function testeAsync() As Task(Of String)
Dim placeholder2 As JavascriptResponse = Await nav.EvaluateScriptAsync("document.getElementById('email').placeholder")
If (placeholder2.Success) Then
MsgBox(placeholder2.Result)
End If
End Function
But when I do a return in the function and call the function in a struct, how do I call waiting for the async function to complete?
This way the sub prints the result before the asynchronous process ends.
Sub testeResultado()
Dim returnedTaskTResult As Task(Of String) = testeAsync()
MsgBox(returnedTaskTResult.ToString)
End Sub

VB.Net to optimize execution by keeping data other than fetching data from database everytime

I am newbie in vb.net I am trying to optimize my code execution that i am working on . i am working on a program having more than 45 forms. In every form its calling the function IsPowerUser to check is power user
If we can store all details about user while logging in , then we need to use these values every time when we needed instead of collecting data from database . May be this question belongs to VB Basics.
You can use lazy initialization to postpone the database call until you actually need the information (if ever), and then store the value for re-use.
Private _isPowerUser As New Lazy(Of Boolean)(AddressOf GetIsPowerUser)
Public Function IsPowerUser() As Boolean
Return _isPowerUser.Value
End Function
Private Function GetIsPowerUser() As Boolean
' Retrieve the information from the database and return
Return True Or False
End Function
In this sample, _isPowerUser is a backing field that uses lazy initialization. It is first initialized when someone calls IsPowerUser. Under the covers, Lazy(Of T) then calls the delegate GetIsPowerUser() that does all the heavy lifting to retrieve the value from the database.
Lazy(Of T) only needs to call GetIsPowerUser() once.
In other words: any consecutive calls to IsPowerUser() do not trigger GetIsPowerUser().
EDIT (1/2)
Lazy(Of T) was not introduced until .NET 4, so here is a code sample that works for .NET 2 and above:
Private _isPowerUser As Boolean?
Private lazyLock As New Object
Public Function IsPowerUser() As Boolean
If Not _isPowerUser.HasValue Then
SyncLock lazyLock
If Not _isPowerUser.HasValue Then
_isPowerUser = GetIsPowerUser()
End If
End SyncLock
End If
Return _isPowerUser.Value
End Function
Private Function GetIsPowerUser() As Boolean
' Retrieve the information from the database and return
Return True Or False
End Function
EDIT (2/2)
I changed my code sample to work in a multi-user environment:
Private _powerUsers As New Dictionary(Of Long, Boolean)
Private ReadOnly LazyLock As New Object
Public Function IsPowerUser(userId As Long) As Boolean
If Not _powerUsers.ContainsKey(userId) Then
SyncLock LazyLock
If Not _powerUsers.ContainsKey(userId) Then
_powerUsers.Add(userId, GetIsPowerUser(userId))
End If
End SyncLock
End If
Return _powerUsers.Item(userId)
End Function
Private Function GetIsPowerUser(UId As Long) As Boolean
' Retrieve the information from the database and return
Return True Or False
End Function
Usage example:
Dim Steven As Long = 454151
If IsPowerUser(Steven) Then
Console.WriteLine("Steven is a power user.")
Else
Console.WriteLine("Steven is a normal user.")
End If

Start task without waiting

I would like to start a background task without using the Await keyword. Instead I want to monitor the task at various points and restart it when necessary to update information in the background. Here is the method I am trying to call:
Public Async Function UpdateVehicleSummaries(p_vehicleID As Int32) As Task(Of Boolean)
Dim tempVehicle As Koolsoft.MARS.BusinessObjects.Vehicle
For Each tempVehicle In Vehicles
If p_vehicleID = 0 Or p_vehicleID = tempVehicle.VehicleID Then
Await UpdateVehicleStats(tempVehicle)
End If
Next
Return True
End Function
The code I am trying to start the task doesn't seem to work and I'm not sure how to provide the parameter. I get an error that "Task(Of Boolean) cannot be converted to System.Action and or an error on the parameter"
Dim tempTask As Task
tempTask = New Task(UpdateVehicleSummaries(tempVehicleID))
tempTask.Start()
Any help would be appreciated.
Since UpdateVehicleSummaries is already asynchronous, you should be abel to just do:
Dim tempTask As Task(Of Boolean) = UpdateVehicleSummaries(tempVehicleID)
The returned Task(Of T) will be "hot" (running), but shouldn't block, as the Await call will immediately return control flow to the caller at that point.
A more typical use of this method, if you need to perform other work while this runs, would be to do the following:
Dim tempTask = UpdateVehicleSummaries(tempVehicleID)
' Do your other work
Dim success = Await tempTask ' Get the boolean result asynchronously...
' use the result

How to throttle concurrent Async webrequests

I often need to make a large number of webrequests, without overloading the network
I currently do this by running synchronous requests in parallel, utilizing ThreadPool.SetMinThreads and MaxDegreeOfParallelism to exactly specify how many requests run concurrently
Now this works just fine, but it feels wrong.
I would really like to utilize async methods, but i cant work out how to limit the number of concurrent requests.
A simplified example of my parallel way of doing this( using a webclient and no error handling for brevity):
Private Function SearchSitesForKeywordInParallel(ByVal keyword As String, ByVal sites As String(), ByVal maxConcurrency As Integer) As String()
Dim po As New ParallelOptions
po.MaxDegreeOfParallelism = maxConcurrency
Threading.ThreadPool.SetMinThreads(maxConcurrency, 2)
Dim sitesContainingKeyword As New Concurrent.ConcurrentBag(Of String)
Parallel.For(0, sites.Count, po, Sub(i)
Dim wc As New Net.WebClient
wc.Proxy = Nothing
Dim pageSource As String = wc.DownloadString(sites(i))
If pageSource.Contains(keyword) Then
sitesContainingKeyword.Add(sites(i))
End If
End Sub)
Return sitesContainingKeyword.ToArray
End Function
This is a blocking function, which is what i require.
Now i have tested the webclient.downloadStringAsync method in a regular for loop, and it will fire all the requests pretty much at once, overloading the network.
What i would like to do is initially make X requests, then make new ones as each response comes back.
I am fairly sure tasks is the way to go, and im positive a have read some very nice implementations in c#, but my c# experience is limited, and i have a hard time translating c# lambadas to vb.net.
I am also limited to vs2010 and .net4, so the niceties of .net4.5 async await are not an option for me.
Any help very much appreciated
Not sure, if I understand completey, what exactly you want to achieve, but if you want to use aync methods, you can do it like this:
Dim google As String = "http://www.google.com/#&q="
Dim qsites As New Concurrent.ConcurrentQueue(Of String)
For Each k In {"foo", "bar", "john", "jack", "stackoverflow", "basic", "ship", "car", "42"}
qsites.Enqueue(google & k)
Next
Dim cde As New System.Threading.CountdownEvent(qsites.Count)
Dim strings As New Concurrent.ConcurrentBag(Of String)
Dim completedhandler = Sub(wco As Object, ev As Net.DownloadStringCompletedEventArgs)
Dim wc = DirectCast(wco, Net.WebClient)
Debug.Print("got one!")
strings.Add(ev.Result)
cde.Signal()
Dim s As String = String.Empty
If qsites.TryDequeue(s) Then
Debug.Print("downloading from {0}", s)
wc.DownloadStringAsync(New Uri(s))
End If
End Sub
Dim numthreads As Integer = 4
System.Threading.Tasks.Task.Factory.StartNew(Sub()
For i = 1 To numthreads
Dim s As String = String.Empty
If qsites.TryDequeue(s) Then
Dim wc As New Net.WebClient
wc.Proxy = Nothing
AddHandler wc.DownloadStringCompleted, completedhandler
Debug.Print("downloading from {0}", s)
wc.DownloadStringAsync(New Uri(s))
End If
Next
End Sub)
cde.Wait()
You only need to "start" the async downloads in a different thread/task because (afaik) the WC's downloadcompleted events fire in the UI thread (or currentsync..context) and the cde.wait would then not allow the events to be handled.
I just want to add another answer to this as I have recently solved a similar problem (note the code snippet is in C#, but should give the idea).
I used to have number of parallel http synchronous requests sent to http server on different thread and used to limit the number of requests I sent using semaphore.
Now, I have adapted to new TPL (c# 5.0 - aysn/await - quite handy (basically continuation introduced in TPL sound natural to me - and with async/await it has become much easier to use)), to invoke network I/O asynchronously.
i.e. ideally now I will be using only one thread in caller (unless I really need to get results before continuing), and let .net, os and I/o completion port threads work together to invoke my continuation code in thread pool to complete operation (basically 'callback' in APM, on completed event in event based pattern, 'continuation' in TPL, code after await in C# 5.0 (4.5 .net))
The principle I followed when I have embraced async i/o is simple - don't let thread wait and waste CPU and resources, unless it is really necessary!
You can do this asynchronously in VB.NET using the Wintellect Powerthreading library's AsyncEnumerator class, which you can get from NuGet.
This gives you some of the functionality of Await but works in VS2010 with .Net 2.0 to 4.0 while giving you an upgrade path to the 4.5 async features.
The downside is that the WebClient async methods require an EAP-to-APM shim based on Task<> to be used with AsyncEnumerator, so the code is quite a lot more complicated.
The simplest way to control the number of concurrent requests is to initiate X async operations, then just initiate another every time one completes.
Example code:
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Imports System.Threading.Tasks
Imports System.Net
Imports Wintellect.Threading.AsyncProgModel
Module TaskExtension
REM http://msdn.microsoft.com/en-us/library/hh873178.aspx
<Extension()>
Public Function AsApm(Of T1)(ByVal task As Task(Of T1), callback As AsyncCallback, state As Object) As IAsyncResult
If (task Is Nothing) Then
Throw New ArgumentNullException("task")
End If
Dim tcs = New TaskCompletionSource(Of T1)(state)
task.ContinueWith(Sub(t As Task(Of T1))
If (t.IsFaulted) Then
tcs.TrySetException(t.Exception.InnerExceptions)
ElseIf t.IsCanceled Then
tcs.TrySetCanceled()
Else : tcs.TrySetResult(t.Result)
End If
If (Not callback Is Nothing) Then
callback(tcs.Task)
End If
End Sub, TaskScheduler.Default)
Return tcs.Task
End Function
End Module
Module ApmAsyncDownload
Public Function DownloadStringAsync(url As Uri) As Task(Of String)
Dim tcs As New TaskCompletionSource(Of String)
Dim wc As New WebClient()
AddHandler wc.DownloadStringCompleted, Sub(s As Object, e As System.Net.DownloadStringCompletedEventArgs)
If (Not (e.Error Is Nothing)) Then
tcs.TrySetException(e.Error)
ElseIf e.Cancelled Then
tcs.TrySetCanceled()
Else : tcs.TrySetResult(e.Result)
End If
End Sub
wc.DownloadStringAsync(url)
Return tcs.Task
End Function
Public Function BeginDownloadString(url As Uri, callback As AsyncCallback, state As Object) As IAsyncResult
Return DownloadStringAsync(url).AsApm(callback, state)
End Function
Public Function EndDownloadString(asyncResult As IAsyncResult) As String
Dim castToTask As Task(Of String) = asyncResult
Return castToTask.Result
End Function
End Module
Public Class AsyncIterators
Private Shared Iterator Function SearchUrl(ae As AsyncEnumerator(Of Boolean), keyword As String, uri As Uri) As IEnumerator(Of Int32)
ae.Result = False
ApmAsyncDownload.BeginDownloadString(uri, ae.End(0, AddressOf ApmAsyncDownload.EndDownloadString), Nothing)
Yield 1
If (ae.IsCanceled()) Then
Return
End If
Try
Dim page As String = ApmAsyncDownload.EndDownloadString(ae.DequeueAsyncResult)
ae.Result = page.Contains(keyword)
Catch ex As AggregateException
End Try
End Function
Public Shared Iterator Function SearchIterator(ae As AsyncEnumerator(Of List(Of String)), keyword As String, urls As List(Of Uri)) As IEnumerator(Of Int32)
ae.Result = New List(Of String)
'Control how many searches are started asynchonously
Dim startSearches = Math.Min(3, urls.Count)
Dim enumerator = urls.GetEnumerator
Dim toBeCompleted = urls.Count
Do Until (toBeCompleted <= 0)
While (startSearches > 0)
If enumerator.MoveNext Then
Dim subAe = New AsyncEnumerator(Of Boolean)()
subAe.SyncContext = Nothing
subAe.BeginExecute(SearchUrl(subAe, keyword, enumerator.Current), ae.End(0, Function(ar As IAsyncResult) As AsyncEnumerator.EndObjectXxx
subAe.EndExecute(ar)
End Function), enumerator.Current)
End If
startSearches = startSearches - 1
End While
'Wait for first async search to complete
Yield 1
toBeCompleted = toBeCompleted - 1
If (ae.IsCanceled()) Then
Exit Do
End If
'Get result of the search and add to results
Dim result = ae.DequeueAsyncResult()
Dim completedAe = AsyncEnumerator(Of Boolean).FromAsyncResult(result)
If (completedAe.EndExecute(result)) Then
Dim uri As Uri = result.AsyncState
ae.Result.Add(uri.OriginalString)
End If
'Start 1 more search
startSearches = startSearches + 1
Loop
End Function
End Class
Module Module1
Sub Main()
Dim searchAe = New AsyncEnumerator(Of List(Of String))()
searchAe.SyncContext = Nothing
Dim urlStrings = New List(Of String) From {"http://www.google.com", "http://www.yahoo.com", "http://www.dogpile.com"}
Dim uris = urlStrings.Select(Function(urlString As String) As Uri
Return New Uri(urlString)
End Function).ToList()
For Each Str As String In searchAe.EndExecute(searchAe.BeginExecute(AsyncIterators.SearchIterator(searchAe, "search", uris), Nothing, Nothing))
Console.WriteLine(Str)
Next
Console.ReadKey()
End Sub
End Module
And I now see what you mean about translating c# lambdas!

Web Service return value issue in VB.net

I have a Web Service with Just one Web Method that returns a boolean value and a function
When a client application consumes this web Service, I first want to return a value true and then continue with the remaining process.
I was googling and didn't find anything that I can understand. Please help me out. In one of the message posts I saw threading as one option. I tried that too.
Below is the code for the same I commented the threading part please help
<WebMethod()> _
Public Function HelloWorld(ByVal str As String) As Boolean
Dim status As Boolean = False
If str <> "" Then
'Dim t As New Thread(AddressOf ReturnWSStatus)
't.Start()
Me.DoItNow()
End If
Return status
End Function
Public Shared Function ReturnWSStatus() As Boolean
Return True
'Thread.Sleep(0)
End Function
You would need to do something like this:
<WebMethod()> Public Function HelloWorld(ByVal str As String) As Boolean
Dim status As Boolean = False
If str <> "" Then
Dim t As New Thread(AddressOf DoItNow)
t.Start()
return true
End If
End Function
Strictly speaking, the web method doesn't return before the additional processing starts, but it does return immediately after the new thread is launched, so the time difference is very small. Once you return from a method its execution is pretty much done, so whatever you want it to start needs to be started before the Return statement.