batmaci batmaci - 1 month ago 26
Vb.net Question

Asynchronous File I/O using FileStream is blocking UI thread on WPF

I am trying to copy from one folder to another folder and following the msdn article I tried to implement an asnyc call on my WPF application. But I am not sure what I am doing wrong it blocks UI thread and I dont think this first sample is asnyc.

here is my code

Dim tasks = myImages.Select(Async Function(x)
Dim result As Image
Try

result = Await CopyImage(x, If(cts IsNot Nothing, cts.Token, Nothing))

Catch ex2 As Exception

End Try

ProgressValue += 1
CompletedText = ProgressValue.ToString() & " of " + MaxValue.ToString() & " images completed."
Return result
End Function)
Dim results = Await Task.WhenAll(tasks)

Public Async Function CopyImage(Image As Image, ct As CancellationToken) As Task(Of Image)
If ct.IsCancellationRequested Then
Return Nothing
End If

Await _mutex.WaitAsync()
Dim SourceMainDirectory As String = "\\Server\Folder1"
Dim DestinationMainDirectory As String = = "\\Server\Folder2"

Dim Path = Image.Path.Replace("/", "\")
Dim Data As String() = Path.Split("\")
Dim Folder As String
Dim ImageName As String
If Data IsNot Nothing AndAlso Data.Length > 1 Then
Folder = Data(0)
ImageName = Data(1)
End If

Dim ImgFullSource = SourceMainDirectory + Folder + "\" + ImageName
Dim ImgFullDest = DestinationMainDirectory + Folder + "\" + ImageName

Try

Using SourceStream As FileStream = File.Open(ImgFullSource, FileMode.Open)
Using DestinationStream As FileStream = File.Create(ImgFullDest)
Await SourceStream.CopyToAsync(DestinationStream, 81920, ct)
Return Image
End Using
End Using

Catch ex As OperationCanceledException
Return Nothing
Catch ex As Exception
Return Nothing
Finally
_mutex.Release()
End Try

Return Nothing
End Function


ProgressValue above is raised to update my progressbar Value. This Code works fine without blocking UI thread and perfectly updates progress asynchronously If I use myHttpClient.GetAsync method to verify same images on the web, for example

Dim tasks = myImages.Select(Async Function(x)
Dim result As Image
Try

result = Await testUrl_async_cancel(x, If(cts IsNot Nothing, cts.Token, Nothing))
Catch ex2 As Exception

End Try

ProgressValue += 1
CompletedText = ProgressValue.ToString() & " of " + MaxValue.ToString() & " images completed."
Return result
End Function)
Dim results = Await Task.WhenAll(tasks)

Async Function testUrl_async_cancel( ByVal myImage As Image, ByVal ct As CancellationToken) As Task(Of AEL)

If ct.IsCancellationRequested Then
Return Nothing
End If

Await _mutex.WaitAsync()

Dim myHttpClient As New HttpClient()
Dim myHttpResponse As HttpResponseMessage

myHttpClient.BaseAddress = New Uri(imageUrlD)


Try
myHttpResponse = Await myHttpClient.GetAsync(myImageUrl, ct)
Catch ex As OperationCanceledException
myHttpResponse = Nothing
Catch ex As Exception
myHttpResponse = Nothing
Finally
_mutex.Release()
End Try

If myHttpResponse IsNot Nothing AndAlso myHttpResponse.IsSuccessStatusCode Then
Return Nothing
Else
Return myImage
End If


End Function


So it should be something to do with the CopyImage function and respectively using the sourcestream what blocks the UI thread as all other code is the same. How can I make this code async that wont block the UI thread on WPF?

Answer

This behavior is due to an oddity in file streams. File.Open and File.Create can only return synchronous file streams.

To get a truly asynchronous file stream, you have to use the FileStream constructor and either pass true for the isAsync parameter, or include the FileOptions.Asynchronous flag in the options parameter. You must call a constructor overload with either isAsync or options, or else the file stream will be synchronous.