Pasek postępu z асинхронностью w aplikacji FsXaml

0

Pytanie

W moim programie F# (FsXaml/Kod tyłu) chciałbym wykorzystać wskaźnik wykonania bez użycia tła pulpitu, jak to zrobić w C#. Opierając się na artykule w Internecie (link tutaj), próbowałem użyć asynchroniczne procesy robocze.

Stworzyłem w oparciu o kod (w pewnym stopniu) na przykładach z artykułu powyżej, ale nie pracował tak, jak się spodziewałem. Aktualny przepływ (wątek interfejsu użytkownika) nadal jest zablokowany, jak gdyby tam nie było asynchronicznego kodu. Żadnego przełączania na wątek w tle nie dzieje. Pasek postępu jest aktywowany dopiero po zakończeniu długiej operacji. Usuwanie funkcji onThreadPool nie ma żadnego efektu.

Moje pytanie: co jest nie tak w moim kodzie i jak to naprawić?

type MainWindowXaml = FsXaml.XAML<"XAMLAndCodeBehind/MainWindow.xaml">

type MainWindow() as this =

    inherit MainWindowXaml()

    //....some code....

    let ButtonClick _ = 
   
        //....some code....
       
        let longRunningOperation() = //....some long running operation (reading from Google Sheets)....            
             
        let progressBar() = this.ProgressBar.IsIndeterminate <- true     

        let doBusyAsync progress operation =  
            progress
            async
                {   
                  do! operation
                }
            |> Async.StartImmediate 
    
        let onThreadPool operation =
            async
                {    
                  let context = System.Threading.SynchronizationContext.Current
                  do! Async.SwitchToThreadPool()
                  let! result = operation
                  do! Async.SwitchToContext context
                  return result
                } 
    
        let asyncOperation progress operation =   
            async { operation } 
            |> onThreadPool
            |> doBusyAsync progress 
    
        (progressBar(), longRunningOperation()) ||> asyncOperation 
      
    do
        //....some code....
        this.Button.Click.Add ButtonClick
asynchronous f# fsxaml
2021-11-23 23:13:28
2

Najlepsza odpowiedź

3

W kodzie jest kilka błędów.

  • Po pierwsze, w progressBar(), longRunningOperation() rzeczywiście wywołać długotrwałą operację, i dlatego to wszystko uruchamia się tutaj. (O ile mogę się domyślić z twojego niekompletnego przykład, to po prostu wywołanie funkcji, a nie inna operacja asynchroniczna).

  • Następnie przekazuje wyniki operation i progress wokół, ale to tylko unit wartości, które tak naprawdę nic nie robią.

  • Zatem operacja asynchroniczna async { operation } że można przejść do onThreadPool w ogóle nic nie robi.

  • W doBusyAsyncużywasz Async.StartImmediate aby wykonać operację blokadą sposób (tak, że to hamował przepływ, nawet gdyby wykonywał jakąś prawdziwą operację).

  • Oprócz blokady, również nie trzeba async { do! operation } bo to jest odpowiednik po prostu operation.

W ten sposób twój kod w jakiś sposób stał się zbyt skomplikowane. Należy uprościć go do czegoś bardzo prostego, jako pierwszy krok. U mnie nie ma żądanego ustawienia, aby spróbować, ale myślę, że coś takiego jak poniżej powinno zadziałać:

let ButtonClick _ = 
  let longRunningOperation() = 
    // some long-running operation

  let asyncOperation() = async {
    // Start the progress bar here
    let context = System.Threading.SynchronizationContext.Current
    do! Async.SwitchToThreadPool()
    let result = longRunningOperation()
    do! Async.SwitchToContext context
    // Display the 'result' in your user interface
    // Stop the progress bar here
  }

  Async.Start(asyncOperation)

Usunąłem wszystkie zbędne funkcje i przesyłanie parametrów i maksymalnie uproszczone jej - to jest po prostu twoja długa operacja, która jest wywoływana bezpośrednio z async jak tylko przełączy się w puli wątków. Otrzymasz swój wynik i po powrocie do pierwotnego kontekstu, można go wyświetlić w swoim interfejsie użytkownika. Najlepiej, byś zrobił longRunningOperation sam w sobie asynchroniczny (i nadaj mu z pomocą let!ale wszystko musi się udać.

2021-11-24 00:15:43
0

Podsumowując, jestem rozszerzył kod Tomasza Петржичека kodem, związanym z długotrwałym zabiegiem, na podstawie komentarza Jima Фоя (o zwrocie w wątku interfejsu użytkownika). Kod teraz działa jak zaklęcie. Dziękuję Tomasza Петржичека za uczciwe i szczegółową odpowiedź.

    let low = string (this.TextBox2.Text)
    let high = string (this.TextBox3.Text)
    let path = string (this.TextBox4.Text)

    (* longRunningOperation() replaced by textBoxString4() and textBoxString3() 
       based on the comment by Jim Foye
    
    let longRunningOperation() = 
        async
            {
              match textBoxString4 low high >= 0 with
              | false -> this.TextBox1.Text <- textBoxString3 low high path 
              | true  -> this.TextBox1.Text <- "Chybný rozdíl krajních hodnot"        
            }
    *)

    let textBoxString4() = 
        async
            {
              let result = textBoxString4 low high
              return result
            }                  
                           
    let textBoxString3() =        
        async
            {
              //the actual long running operation (reading data 
              //from Google Sheets)
              let result = textBoxString3 low high path 
              return result
            }  

    let asyncOperation() = 
        async
            {
              let context = System.Threading.SynchronizationContext.Current
              this.ProgressBar2.IsIndeterminate <- true
              do! Async.SwitchToThreadPool()
              (*let! result = longRunningOperation() throws an exception 
              "The calling thread cannot access this object because
               a different thread owns it." 
              *)
              let! result4 = textBoxString4()  
              let! result3 = textBoxString3()  
              do! Async.SwitchToContext context
              match result4 >= 0 with
              | false -> this.TextBox1.Text <- result3
              | true  -> this.TextBox1.Text <- "Chybný rozdíl krajních hodnot" 
              this.ProgressBar2.IsIndeterminate <- false
            } 
    Async.StartImmediate(asyncOperation())//not working with Async.Start
                                             
2021-11-24 18:29:36

W innych językach

Ta strona jest w innych językach

Русский
..................................................................................................................
Italiano
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................