Задача не обновляет пользовательский интерфейс в WPF

#c# #wpf #multithreading #task

#c# #wpf #многопоточность #задача

Вопрос:

Я написал этот код:

 public double SumRootN(int root)
{
    double result = 0;
    for (int i = 1; i < 10000000; i  )
    {
        tokenSource.Token.ThrowIfCancellationRequested();
        result  = Math.Exp(Math.Log(i) / root);
    }
    return resu<
}

private void btnclick_Click(object sender, RoutedEventArgs e)
{
    tokenSource = new CancellationTokenSource();
    txttest.Text = "";

    var watch = Stopwatch.StartNew();
    List<Task> tasks = new List<Task>();
    var ui = TaskScheduler.FromCurrentSynchronizationContext();
    for (int i = 2; i < 20; i  )
    {
        int j = i;
        var compute = Task.Factory.StartNew(() =>
        {
            return SumRootN(j);
        }, tokenSource.Token);

        tasks.Add(compute);

        var displayResults = compute.ContinueWith(
                                resultTask =>
                                   txttest.Text
                                     = "root "   j.ToString()   " "  
                                        compute.Result.ToString()  
                                        Environment.NewLine,
                                CancellationToken.None,
                                TaskContinuationOptions.OnlyOnRanToCompletion,
                                ui);             
    }      
}
 

Это работает в WPF, но когда я написал этот код таким образом;

 tokenSource = new CancellationTokenSource();
var watch = Stopwatch.StartNew();
List<Task> tasks = new List<Task>();
var ui = TaskScheduler.FromCurrentSynchronizationContext();

Report 
    = ((Microsoft.Office.Interop.Excel.Range)_sheet.Cells[row, "B"]).Value2;

var compute = Task.Factory.StartNew(() =>
             {
                return Report;                             
             }, tokenSource.Token);

            tasks.Add(compute);

            var displayResults
                = compute.ContinueWith(resultTask =>
                     txtReport.Text 
                         = compute.Result.ToString()  
                           Environment.NewLine,
                     CancellationToken.None,
                     TaskContinuationOptions.OnlyOnRanToCompletion,
                     ui);
 

Он не работает, хотя txtReport.Text имеет правильные значения, но он не отображается в середине операции, но когда операция заканчивается txtReport , отображаются ее значения

Почему этот код не работает?

Комментарии:

1. Реализует ли ваша модель представления INotifyPropertyChanged интерфейс?

2. Вам все равно нужно сообщить XAML, что свойство изменилось.

3. пожалуйста, опишите меня немного подробнее…

4. @ChrisF: я не вижу здесь отсутствующего INPC. OP устанавливает txttest. Текст напрямую. Нет необходимости в PropertyChanged-событии. Я ошибаюсь?

5. @HCL — Ah — я понимаю вашу точку зрения. Мои комментарии выше неверны — я неправильно понял, какое свойство обновлялось.

Ответ №1:

ContinueWith создает продолжение, которое выполняется асинхронно при завершении целевой задачи. Т.е. он обновит результат только SumRootN после завершения вашей операции.

Ответ №2:

Я никогда не работал с TaskScheduler, поэтому я не могу дать вам прямой отзыв, что не так. Но из описания вашей проблемы я совершенно уверен, что проблема в том, что пользовательский интерфейс заблокирован. Пока ваш код не завершен, пользовательский интерфейс обновляться не будет.

Для выполнения трудоемких операций выполняйте их с помощью BackgroundWorker. Однако позаботьтесь о том, чтобы вы не могли напрямую устанавливать значения своих элементов управления из асинхронного. Либо используйте диспетчер для маршрутизации вашей команды в UI-thread, либо используйте ProgressChanged-event.

Следующий код показывает, как использовать BackgroundWorker:

 BackgroundWorker bgWorker = new BackgroundWorker() { WorkerReportsProgress=true};  
bgWorker.DoWork  = (s, e) => {      
    // Do here your calculations
    // Use bgWorker.ReportProgress(); to report the current progress  
};  
bgWorker.ProgressChanged =(s,e)=>{      
    // Here you will be informed about progress and here it is save to set the labels value. 
};  
bgWorker.RunWorkerCompleted  = (s, e) => {      
// Here you will be informed if the job is done. 
};  
bgWorker.RunWorkerAsync();  
 

Комментарии:

1. но я использую подобный код в winforms, и он работает. используйте 1’var ui = TaskScheduler. FromCurrentSynchronizationContext(); ‘ для синхронизации потоков. почему это не работает?

Ответ №3:

Для этого есть несколько советов по устранению неполадок… Возможно, вам придется сначала ответить на эти вопросы…

  1. Где на самом деле находится ваш код? В вызове Button_click()?
  2. Когда вы ставите brekapoint в свой код TaskScheduler.FromCurrentSynchronizationContext(); , какой SynchronizationContext объект вы видите? Dispatcher тип? Или какой-то другой тип?
  3. Есть ли конкретная причина для использования TPL? Не можете ли вы использовать BackgroundWorker и Dispatcher сопрягать?