Public Shared Async Function getMarketDetailFromAllExchangesAsync() As Task
Dim taskList = New List(Of Task)
Dim starttime = jsonHelper.currentTimeStamp
LogEvents("Start Getting Market Detail of All")
For Each account In uniqueAccounts().Values
Dim newtask = account.getMarketInfoAsync().ContinueWith(Sub() account.LogFinishTask("GetMarketDetail", starttime))
taskList.Add(newtask)
'newtask.ContinueWith(Sub() LogEvents(account.ToString))
Next
Await Task.WhenAll(taskList.ToArray)
Dim b = 1
End Function
Is there a way to do .ContinueWith(Sub() account.LogFinishTask("GetMarketDetail", starttime)) with addressOf instead?
How?
You need to create a method which satisfies any of existed ContinueWith overloads.
In your particular case it should satisfy a signature of Action(Of Task).
But because in ConitnuesWith you are using account instance, you will not be able to use AddressOf with method of the class where loop is executed.
As workaround you can create required method in class of account
Public Class Account
Public Sub LogFinishedMarketDetail(task As Task)
Dim starttime = jsonHelper.currentTimeStamp
Me.LogFinishTask("GetMarketDetail, starttime")
End Sub
End Class
Usage
For Each account In uniqueAccounts().Values
Dim newtask =
account.getMarketInfoAsync().ContinueWith(AddressOf account.LogFinishedMarketDetail)
taskList.Add(newtask)
Next
Suggestion - set Option Strict to On - will saves developer time by displaying possibly errors during compile time, instead of run-time.
Related
Public Shared Async Function getMarketDetailFromAllExchangesAsync() As Task
Dim taskList = New List(Of Task)
For Each account In uniqueAccounts()
Dim newtask = account.Value.getMarketInfoAsync()
taskList.Add(newtask)
Next
Await Task.WhenAll(taskList.ToArray)
Dim b = 1
End Function
The code work just fine.
However, I want to log every time a task is done
So I did
newtask.ContinueWith(Async Function(x) LogEvents(account.ToString))
LogEvents is a normal function. I got 2 error
How exactly should I do that?
I did it this way
Public Shared Async Function getMarketDetailFromAllExchangesAsync() As Task
Dim taskList = New List(Of Task)
Dim starttime = jsonHelper.currentTimeStamp
LogEvents("Start Getting Market Detail of All")
For Each account In uniqueAccounts().Values
Dim newtask = account.getMarketInfoAsync().ContinueWith(Sub() account.LogFinishTask("GetMarketDetail", starttime))
taskList.Add(newtask)
'newtask.ContinueWith(Sub() LogEvents(account.ToString))
Next
Await Task.WhenAll(taskList.ToArray)
Dim b = 1
End Function
If anyone knows how to do so without a lambda that'll be great.
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.
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!
I put together this bit of code from a few other samples, and I am getting an error I cant understand. On this line in the code below, on the word Observer,
Dim Results As ManagementObjectCollection = Worker.Get(Observer)
I get the error
"Value of type 'System.Management.ManagementOperationObserver' cannot be converted to 'Integer'"
Can somebody explain what this means?
There are two signatures for ManagementObjectSearcher.Get(), one has no parameters and the other has one parameter, a ManagementOperationObserver for async operation. That is what I am providing, yet the error indicates conversion involving an integer?
Public Shared Sub WMIDriveDetectionASYNC(ByVal args As String())
Dim Observer As New ManagementOperationObserver()
Dim completionHandler As New MyHandler()
AddHandler Observer.Completed, AddressOf completionHandler.Done
Dim Machine = "192.168.0.15"
Dim Scope = New ManagementScope("\\" & Machine & "\root\cimv2")
Dim QueryString = "select Name, Size, FreeSpace from Win32_LogicalDisk where DriveType=3"
Dim Query = New ObjectQuery(QueryString)
Dim Worker = New ManagementObjectSearcher(Scope, Query)
Dim Results As ManagementObjectCollection = Worker.Get(Observer) 'use parameter to make async
For Each item As ManagementObject In Results
Console.WriteLine("{0} {2} {1}", item("Name"), item("FreeSpace"), item("Size"))
Dim FullSpace As Long = (CLng(item("Size")) - CLng(item("FreeSpace"))) \ 1000000
Console.WriteLine(FullSpace)
Next
End Sub
Public Class MyHandler
Private _isComplete As Boolean = False
Public Sub Done(sender As Object, e As CompletedEventArgs)
_isComplete = True
End Sub 'Done
Public ReadOnly Property IsComplete() As Boolean
Get
Return _isComplete
End Get
End Property
End Class
Thanks for any advice!
I think that uses a reference type to get the result and put it in the object you sent as a parameter. So I think it just needs to look like:
Worker.Get(Observer)
instead of trying to set something = to that since it isn't a function that returns a value.
Then use the events you hook up to the object to handle whatever you need to do with the items you find.
i have 20,000 items in a queue, and i want to process them using the threadpool.
will this be the best way to do it?
for i as integer = 0 to 19999
ThreadPool.QueueUserWorkItem (PerformTask, ListTask(i))
next
Sub PerformTask(i as string)
' do the work here
end sub
How can i return or set ui control from the PerformTask sub?
You cannot.
However, you can allocate a container (array, list) with a different slot for each result, and write into it. Alternatively, you could pass an object into the worker method that holds both the input and the result. I’d use this method:
Class TaskObject
Dim Input As String
Dim Result As Whatever
End Class
Dim tasks As TaskObject() = New TaskObject(20000) { }
For i as Integer = 0 to tasks.Length - 1
ThreadPool.QueueUserWorkItem(PerformTask, tasks(i))
next
Sub PerformTask(arg As Object)
Dim task As TaskObject = DirectCast(arg, TaskObject)
' do the work here
end sub
Unrelated: you should always enable Option Strict in your projects. No exception. Your code has type errors that the compiler should detect.