2016-06-08 2 views
1

У меня долгое задание, и я не хочу блокировать пользовательский интерфейс, поэтому у меня есть DispatcherTimer, и я использую его, чтобы проверить свойство задачи IsCompleted, но это вызывает какой-то тупик, потому что мое приложение перестает отвечатьМожно ли опросить задачу для завершения?

public partial class MainWindow : Window 
{ 
    DateTime beginFirstPhase; 
    DateTime beginSecondPhase; 
    DispatcherTimer dispatcherTimer = new DispatcherTimer(); 
    IEnumerable<string> collection; 
    Task firstPhaseTask; 
    Task secondPhaseTask; 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void button_Click(object sender, RoutedEventArgs e) 
    { 
     progressTxtBox.AppendText("Entering button click event handler\n"); 
     beginFirstPhase = DateTime.Now; 

     dispatcherTimer.Tick += DispatcherTimer_Tick_FirstPhase; 
     dispatcherTimer.Interval = new TimeSpan(0, 0, 5); 
     dispatcherTimer.Start(); 

     progressTxtBox.AppendText("Begining First Phase now\n"); 

     firstPhaseTask = Task.Factory.StartNew(() => 
      /*this is basically a big linq query over a huge collection of strings 
      (58 thousand+ strings). the result of such query is stored in the field named 
      collection, above*/), TaskCreationOptions.PreferFairness); 
     progressTxtBox.AppendText("Awaiting First Phase completion...\n"); 
    } 

    private void DispatcherTimer_Tick_FirstPhase(object sender, EventArgs e) 
    { 
     TimeSpan span = DateTime.Now - beginFirstPhase; 
     //not even the line bellow is executed. 
     statusTextBlock.Text = $"Running: {span.ToString()}"; 

     if (firstPhaseTask.IsCompleted) 
     { 
      dispatcherTimer.Stop(); 
      progressTxtBox.AppendText($"First Phase completed in {span.ToString()}\n"); 
      secondPhase(); 
     } 
    } 

    private void secondPhase() 
    { 
     beginSecondPhase = DateTime.Now; 

     progressTxtBox.AppendText("Begining Second Phase now\n")); 

     dispatcherTimer.Tick -= DispatcherTimer_Tick_FirstPhase; 
     dispatcherTimer.Tick += DispatcherTimer_Tick_SecondPhase; 
     dispatcherTimer.Interval = new TimeSpan(0, 0, 5); 
     dispatcherTimer.Start(); 

     int counter = 0; 
     secondPhaseTask = Task.Factory.StartNew(() => 
     { 
      foreach (string str in collection) 
      { 
       Dispatcher.Invoke(() => progressTxtBox.AppendText($"iteration <{counter++}>\n")); 
       IEnumerable<Tuple<string, string> queryResult; // = another linq query 
       foreach (var tuple in queryResult) 
       { 
        Dispatcher.Invoke(() => outputListView.Items.Add($"{tuple.Item1} {tuple.Item2} {str}")); 
       } 
      } 
     }, TaskCreationOptions.PreferFairness); 
    } 

    private void DispatcherTimer_Tick_SecondPhase(object sender, EventArgs e) 
    { 
     TimeSpan span = DateTime.Now - beginSecondPhase; 
     statusTextBlock.Text = $"Running: {span.ToString()}"; 
     if (secondPhaseTask.IsCompleted) 
     { 
      dispatcherTimer.Stop(); 
      progressTxtBox.AppendText($"Second Phase completed in {span.ToString()}\n"); 
      progressTxtBox.AppendText("Execution Complete"); 
     } 
    } 
} 

Что заставляет это блокировать? Does Task.IsCompleted блокирует поток звонящего? Возможно ли вообще опросить такую ​​задачу? если нет, есть ли другой вариант?

EDIT: Уважаемые оцененные члены сообщества StackOverflow, я получаю несколько ответов, которые в основном говорят: «Не делайте этого так, этот путь лучше». Все это очень хорошо продуманные ответы, и я благодарю всех вас за них. Но мой вопрос заключается в следующем: «Можно ли опросить задачу для завершения и как это сделать». Nevermind это плохой дизайн, потому что это не было частью вопроса. Я понимаю, что могу переписать код для использования ждут и асинхронно, но, поскольку я самообучаюсь, я считаю, что это хорошая идея, чтобы изучить, что может сделать язык.

Это как если бы я задал вопрос о том, как работает goto, и все ответы, которые я получил, были почему бы не использовать goto и как его заменить. Я понимаю, что вы все хотите как можно лучше помочь, предоставив мне знания о том, что мой выбор дизайна плох, но, не помогая мне реализовать мой плохой выбор, вы отказываете мне в знаниях о том, как это сделать. И я смиренно верю, что это противоречит духу этого сообщества. Надеюсь, это добавление никого не обидит, у меня нет ничего, кроме уважения ко всем членам сообщества, и я надеюсь, что я сделал это достаточно ясно.

ответ

1

Вы хотите «закрыть» над Task.Run, используя оператор ожидания. Таким образом вы можете сказать пользователю «Ожидание ...», тогда, когда задача завершается, вы автоматически включаете поток Gui. Если вам нужны отчеты о проделанной работе, я думаю, что это делается через класс Progress, но не помню. В любом случае это должно заставить вас закрыть ...

private async void button_Click(object sender, RoutedEventArgs e) 
{ 
    progressTxtBox.AppendText("Entering button click event handler\n"); 
    beginFirstPhase = DateTime.Now; 

    dispatcherTimer.Tick += DispatcherTimer_Tick_FirstPhase; 
    dispatcherTimer.Interval = new TimeSpan(0, 0, 5); 
    dispatcherTimer.Start(); 

    progressTxtBox.AppendText("Begining First Phase now\n"); 
    progressTxtBox.AppendText("Awaiting First Phase completion...\n"); 
    firstPhaseTask =await Task.Factory.StartNew(() => 
     /*this is basically a big linq query over a huge collection of strings 
     (58 thousand+ strings). the result of such query is stored in the field named 
     collection, above*/), TaskCreationOptions.PreferFairness); 
    progressTxtBox.AppendText("First Phase complete...\n"); 

} 

Я также предлагаю изменить результаты, чтобы быть ...

var results =await Task<IEnumerable<OfType>>.Factory.StartNew(() =>{ 
      return context.where(p=>p.field = fieldvalue); 
+0

Это не скомпилировано: 'firstPhaseTask = ждет TaskFactor.StartNew (...)'. Кроме того, я второй этап, который начнется только после первой фазы, будет завершен, иначе объекты будут в несогласованном состоянии. Вот почему мне нужно опросить, если первый этап завершен или нет. – FinnTheHuman

+0

Нет, вам не нужно опросить, если вы используете закрытие, оно не скомпилируется, потому что вам нужно изменить код, чтобы он был похож на мое предложение. Если вам нужен результат запроса, верните результаты, как показано в предложении. Когда компилятор попадает в состояние ожидания, этот поток приостанавливается автоматически до возобновления, которое задает результаты var. Тогда вы находитесь в главной теме, и то же самое происходит, когда 2-й ожидание попадает, если у вас есть «Закрытие» ... –

0

Есть ли причина, что вы не используете async и await?

await Task.Factory.StartNew(() => 
      /*this is basically a big linq query over a huge collection of strings 
      (58 thousand+ strings). the result of such query is stored in the field named 
      collection, above*/), TaskCreationOptions.PreferFairness); 

// StartSecondPhase won't get called until the Task returned by Task.Factory.StartNew is complete 
await StartSecondPhase(); 
+0

Итак, это значит, что я не могу периодически опросить «Задачу» для завершения? Потому что вторая фаза не может начаться, если первая фаза не завершена. – FinnTheHuman

+0

Вы можете опросить, но почему? Нет причин для этого, если вы не хотите прогресса. –

+0

@JohnPeters Мне также нужен текущий прогресс. И если я могу опросить, как мне это сделать? проверка Task.IsCompleted вызывает тупик. – FinnTheHuman

0

Лично я бы подойти к этому так: Найти шоу заняты службы анимации, которая вам нравится, Google является вашим другом.

Призовите занята анимацию (Назовите пользователь ждать, вы можете найти тот, который позволит вам обновить текущее состояние, а)

запустить операцию, как уже было предложено, но с минимальными переделками, как это:

this.BusyService.ShowBusy(); 
Task t = Task.Factory.StartNew(() => 
     PhaseOne(); 
     PhaseTwo(); 
), TaskCreationOptions.PreferFairness); 
t.ContinueWith(()=>{ this.BusyService.HideBusy(); }); 

Я говорю о сервисе для шоубуса, потому что я связываю его с Prism и WPF, но и простой WinForm может сделать трюк, и здесь есть пример примера здесь. Я думаю.

Что произойдет: пользовательский интерфейс заблокирован, но не замерзает, пользователь будет знать, что ему нужно подождать, при обратном вызове задачи вы отпустите пользовательский интерфейс.

Надеюсь, что эта помощь.

EDIT: подход Давай это немного по-другому, вот демо-версию для WinForms в Busy индикатора я говорил: BusySample

Там в BusyForm который только немодальный форма с многострочным текстовым полем, где» напишите свой текст обновления. Плюс есть главная форма, кто использует его, как это:

  private BackgroundWorker worker; 
    private BusyForm busyForm = new BusyForm(); 
    private string progressText; 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     progressText = "Entering button click event handler" + Environment.NewLine; 
     busyForm.SetText(progressText); 
     worker = new BackgroundWorker(); 
     worker.DoWork += worker_DoWork; 
     worker.RunWorkerCompleted += worker_RunWorkerCompleted; 
     worker.RunWorkerAsync(); 
     busyForm.ShowDialog(); 
    } 

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     busyForm.Hide(); 
    } 

    private void worker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     progressText += "Begining First Phase now" + Environment.NewLine; 
     this.Invoke((MethodInvoker)delegate 
     { 
      busyForm.SetText(progressText); 
     }); 
     PhaseOne(); 
     progressText += "First Phase complete..." + Environment.NewLine + "Begining Second Phase now" + Environment.NewLine; 
     this.Invoke((MethodInvoker)delegate 
     { 
      busyForm.SetText(progressText); 
     }); 
     PhaseTwo(); 
     progressText += "Execution Complete"; 
     this.Invoke((MethodInvoker)delegate 
     { 
      busyForm.SetText(progressText); 
     }); 
     System.Threading.Thread.Sleep(2000); //Just adding a delay to let you see this is shown 
    } 

    private void PhaseOne() 
    { 
     System.Threading.Thread.Sleep(2000); 
    } 

    private void PhaseTwo() 
    { 
     System.Threading.Thread.Sleep(2000); 
    } 

BackgroundWorker берет на себя все вещи, и он работает в отдельном потоке, в любом случае вам необходимо обновить текст с помощью:

this.Invoke((MethodInvoker)delegate 
    { 
     busyForm.SetText(progressText); 
    }); 

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

+0

Я не знаю, что такое BusyService. вы можете отправить мне ссылку, пожалуйста? – FinnTheHuman

+0

Это предложение использует продолжения, которые будут блокироваться до завершения. Правильно? Используйте ключевое слово ожидания, как показано ниже. –

+0

@ FinnTheHuman Я обновил свой ответ с полным рабочим образцом того, что я имел в виду для занятого обслуживания. Я написал это на ходу, но вы можете улучшить его или просто найти другого в Интернете. К сожалению, я не могу дать вам ссылку или ту, которую я использую на работе. – Christopher

Смежные вопросы