2012-06-24 2 views
2

Я пытаюсь выполнить итерацию через очередь - взяв 1 элемент из очереди, обработав его в фоновом задании, обновив пользовательский интерфейс, а затем выбрав следующий элемент и т. Д. Проблема заключается в том, что первый элемент обрабатывается в фоновом задании (потоке), но затем последующие элементы обрабатываются в потоке пользовательского интерфейса, блокируя пользовательский интерфейс.Рекурсивная очередь задач

Кто-нибудь знает, почему это происходит и как обойти эту проблему? Мой полный тестовый код приведен ниже. Примечание: этот код предназначен для моего изучения и будущей ссылки - не для любого реального приложения.

public partial class MainWindow : Window 
{ 
    private Queue<int> testQueue = new Queue<int>(); 
    private TaskScheduler uiScheduler; 

    public MainWindow() 
    { 
     InitializeComponent(); 

     this.uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     this.testQueue = new Queue<int>(); 
     this.testQueue.Enqueue(3); 
     this.testQueue.Enqueue(6); 
     this.testQueue.Enqueue(7); 
     this.testQueue.Enqueue(11); 
     this.testQueue.Enqueue(13); 
    } 

    // just a method that takes about 1 second to run on a modern pc 
    private double SumRootN(int root) 
    { 
     double result = 0; 
     for (int i = 1; i < 10000000; i++) 
     { 
      result += Math.Exp(Math.Log(i)/root); 
     } 
     return result; 
    } 

    private void testQueueButton_Click(object sender, RoutedEventArgs e) 
    { 
     this.processQueue(); 
    } 

    private void processQueue() 
    { 
     if (this.testQueue.Count > 0) 
     { 
      int root = this.testQueue.Dequeue(); 
      Task<double>.Factory.StartNew(() => SumRootN(root)) 
       .ContinueWith(t => 
       { 
        this.statusText.Text += String.Format("root {0} : {1}\n", root, t.Result); 
        this.processQueue(); 
       }, uiScheduler); 
     } 
     else 
     { 
      this.statusText.Text += "Done\n"; 
     } 
    } 
} 

ответ

3

Благодарим вас за публикацию репродукции, которая позволила мне отлаживать.

Task.Factory.StartNew запускает вашу задачу на планировщике (factoryScheduler ?? currentTaskScheduler ?? threadPoolScheduler). Вы попали в дело 2: Ваша новая задача наследует планировщик от родителя.

Я заметил ваше любопытное использование рекурсивных вызовов, чтобы имитировать цикл. Если вы сделаете это, как это, проблема уходит:

  Task<double>.Factory.StartNew(() => SumRootN(root)) 
      .ContinueWith(t => 
      { 
       this.statusText.Text += String.Format("root {0} : {1}\n", root, t.Result); 
      }, uiScheduler).ContinueWith(t => { this.processQueue(); }); 
+0

Спасибо большое! Я ошибался. – flolim

+0

Меня тоже. Я отлаживал это, потому что я не мог в это поверить. – usr

1

Это потому, что вы используете TaskScheduler.FromCurrentSynchronizationContext() - вы знаете, что она делает правильно? (Позволяет работать на том же потоке, что называется, в вашем случае UI)

EDIT: USR ответил вам, почему это происходит, но вы также можете сделать это (для квази параллельной обработки):

int root = this.testQueue.Dequeue(); 
    Task<double>.Factory.StartNew(() => SumRootN(root)) 
     .ContinueWith(t => 
     { 
      this.statusText.Text += String.Format("root {0} : {1}\n", root, t.Result); 
     }, uiScheduler); 
    this.processQueue(); 
+0

Да, задача продолжения мчит в потоке пользовательского интерфейса, но первой задачей 'Task .Factory.StartNew (() => SumRootN (корень)) 'должен работать в другом потоке, так как я не задаю для него планировщик задач? – flolim

+0

@flolim - обновил ответ ... –

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