Индикатор выполнения с асинхронностью в приложении FsXaml

0

Вопрос

В моем приложении F# (FsXaml/Код позади) я хотел бы использовать индикатор выполнения без использования фонового рабочего, как я делаю в C#. Основываясь на статье в Интернете (ссылка здесь), я попытался использовать асинхронные рабочие процессы.

Я создал код, основанный (в некоторой степени) на примерах из вышеупомянутой статьи, но он работал не так, как я ожидал. Текущий поток (поток пользовательского интерфейса) по-прежнему заблокирован, как если бы там не было асинхронного кода. Никакого переключения на фоновый поток не происходит. Индикатор выполнения активируется только после завершения длительной операции. Удаление функции onThreadPool не имеет никакого эффекта.

Мой вопрос: что не так в моем коде и как это исправить?

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

Лучший ответ

3

В вашем коде есть ряд ошибок.

  • Во-первых, в progressBar(), longRunningOperation() вы на самом деле вызываете длительную операцию, и поэтому все это запускается здесь. (Насколько я могу догадаться из вашего неполного примера, это просто вызов функции, а не другая асинхронная операция).

  • Затем вы передаете результаты operation и progress вокруг, но это всего лишь unit ценности, которые на самом деле ничего не делают.

  • Следовательно, асинхронная операция async { operation } что вы переходите к onThreadPool вообще ничего не делает.

  • В doBusyAsync, вы используете Async.StartImmediate чтобы выполнить операцию блокирующим способом (так что это заблокировало бы поток, даже если бы он выполнял какую-то реальную операцию).

  • Помимо блокировки, вам также не нужно async { do! operation } потому что это эквивалентно просто operation.

Таким образом, ваш код каким-то образом стал слишком сложным. Вы должны упростить его до чего-то очень простого в качестве первого шага. У меня нет нужной настройки, чтобы попробовать это, но я думаю, что что-то вроде следующего должно сработать:

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)

Я удалил все бесполезные функции и передачу параметров и максимально упростил ее - это просто ваша длительная операция, которая вызывается непосредственно из async как только он переключится в пул потоков. Вы получите свой результат и, вернувшись к исходному контексту, сможете отобразить его в своем пользовательском интерфейсе. В идеале, вы бы сделали longRunningOperation сам по себе асинхронный (и назовите его с помощью let!) но все вышеперечисленное должно сработать.

2021-11-24 00:15:43
0

Подводя итог, я расширил код Томаша Петржичека кодом, связанным с длительной операцией, на основе комментария Джима Фоя (о возврате в поток пользовательского интерфейса). Код теперь работает как заклинание. Я благодарю Томаша Петржичека за его любезный и подробный ответ.

    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

На других языках

Эта страница на других языках

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