Emil
Emil

Reputation: 6893

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?

Upvotes: 0

Views: 408

Answers (2)

Emil
Emil

Reputation: 6893

based on Stephen Cleary's answer. I changed the filestream read and write part as following and it works async without blocking the UI thread. I hope this helps anyone else looking for same solution.

last parameter True is the async parameter as Stephen mentioned.

 Using SourceStream As FileStream = New FileStream(ImgFullSource, FileMode.Open, FileAccess.Read, FileShare.Read, 81920, True)
                Using DestinationStream As FileStream = New FileStream(ImgFullDest, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 81920, True)
                    Await SourceStream.CopyToAsync(DestinationStream, 81920, ct)
                    Return Image
                End Using
            End Using

Upvotes: 1

Stephen Cleary
Stephen Cleary

Reputation: 456717

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.

Upvotes: 7

Related Questions