Problem
I want to call a method asynchronously in a synchronous environment. ie the subsequent lines should execute immediately after the method call regardless how much time the method takes to execute.
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess")
TimeConsumingMethod()
Debug.WriteLine("After LongProcess")
End Sub
Private Sub TimeConsumingMethod()
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(2000)
Debug.WriteLine("Done!!!")
End Sub
When we execute the above code it will result as follows.
Before LongProcess
Done!!!
After LongProcess
But what I want is
Before LongProcess
After LongProcess
Done!!!
The immediate answer
If we ask this question to any developer the immediate answer will be “use threading”.Yes that is correct we can use threading.But is there an other easier solution?
Solution
I was in the category of saying “threading” until I heard about the async method invocation using delegates.Here goes the solution.
- Create a delegate to match with your long running method signature.
- Declare a delegate variable and instantiate it by passing the address of the long running method.
- Call the BeginInvoke Method of the delegate which calls the pointed method in asynchronous fashion.
Public Delegate Sub MyAsyncDelegate()
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess")
Dim del As MyAsyncDelegate = New MyAsyncDelegate(AddressOf TimeConsumingMethod)
del.BeginInvoke(Nothing, Nothing)
Debug.WriteLine("After LongProcess")
End Sub
Private Sub TimeConsumingMethod()
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(20000)
Debug.WriteLine("Done!!!")
End Sub
Passing parameter
You can pass type safe parameters into this methods.For that just change the delegate signature.This automatically ask you to change the function which you are passing into delegate.
Public Delegate Function MyParamAsyncDelegate(ByVal data As String)
Private Function TimeConsumingMethod(ByVal data As String)
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(20000)
Debug.WriteLine("Done!!! Parameter =" & data)
End Function
Private Sub btnCallParam_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess with param")
Dim del As MyParamAsyncDelegate = New MyParamAsyncDelegate(AddressOf TimeConsumingMethod)
del.BeginInvoke(tbData.Text, Nothing, Nothing)
Debug.WriteLine("After LongProcess with param")
End Sub
Processing return values in async method invocation
When we execute a method we might be expecting some values back after the processing.Now lets see how to get the return value.First change the signature of the method such that it returns something.Obviously return the value.
Now comes a new delegate named AsyncCallback which points to the async delegate execution completed method.We need to pass this delegate variable to the InvokeAsync method of our delegate.
The completed method should accept one parameter of type IAsyncResult which is must when we point using AsyncCallback.The importance of this variable comes when we need the return value.
First we need to cast to AsyncResult which is the concrete implementation of IAsyncResult.Then get the AsyncDelegate property which is our delegate and call the EndInvoke method on that.Confused??The change is only in the completed method where we process the return value.See the below code to get clarified.
Public Delegate Function MyParamAsyncDelegate(ByVal data As String) As Integer
Private Function TimeConsumingMethod(ByVal data As String) As Integer
'Imitating time consuming process using Thread.Sleep
System.Threading.Thread.Sleep(5000)
Debug.WriteLine("Done!!! Parameter =" & data)
'Return the length to read by completed method
Return data.Length
End Function
Private Sub btnCallBackParam_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Debug.WriteLine("Before LongProcess")
Dim delCallBack As MyParamAsyncDelegate = New MyParamAsyncDelegate(AddressOf TimeConsumingMethod)
Dim cb As AsyncCallback = New AsyncCallback(AddressOf TimeConsumingMethodCompleted)
Dim res As IAsyncResult = delCallBack.BeginInvoke(tbDataCallBack.Text, cb, Nothing)
Debug.WriteLine("After LongProcess")
End Sub
Private Sub TimeConsumingMethodCompleted(ByVal ar As System.IAsyncResult)
Dim asr As AsyncResult = DirectCast(ar, AsyncResult)
Dim del As MyParamAsyncDelegate = DirectCast(asr.AsyncDelegate, MyParamAsyncDelegate)
Dim result As Integer = del.EndInvoke(ar)
Debug.WriteLine("Async operation completed method.length of the string :" & result.ToString)
End Sub
Uploaded a sample here
No comments:
Post a Comment