2015-11-27 4 views
3

Мне нужно сделать поток пользовательского интерфейса до тех пор, пока массив задач не завершит выполнение. Проблема с приведенным ниже кодом заключается в том, что задачи inturn вызывают поток пользовательского интерфейса для записи в текстовое поле. Как это исправить?WhenAll() не работает должным образом

public partial class FormConsole : Form 
{ 
    public FormConsole() 
    { 
     InitializeComponent(); 
    } 
    void txtSayHello_Click(object sender, EventArgs e) 
    { 
     Class1 objclss = new Class1(); 
     objclss.formConsole = this; 

     Task[] taa = new Task[4]; 
     taa[0] = new Task(() => objclss.DoSomeThigs("Hello world")); 
     taa[1] = new Task(() => objclss.DoSomeThigs("Hello world1")); 
     taa[2] = new Task(() => objclss.DoSomeThigs("Hello world2")); 
     taa[3] = new Task(() => objclss.DoSomeThigs("Hello world3")); 

     foreach(Task task in taa) 
     { 
      task.Start(); 
     } 
     Task.WhenAll(taa); 
     this.txtConsole.AppendText("All threads complete"); 
    } 

    delegate void doStuffDelegate(string value); 

    public void doStuff(string value) 
    {   
     if (System.Windows.Forms.Form.ActiveForm.InvokeRequired && IsHandleCreated) 
     { 
      BeginInvoke(new doStuffDelegate(doStuff), value);   
     } 
     else 
      txtConsole.AppendText(value);   
    } 

} 

public class Class1 
{ 
    public FormConsole formConsole; 
    public void DoSomeThigs(string sampleText) 
    { 
     formConsole.doStuff(sampleText); 
    } 
} 

о/р в настоящее время: Console Redirection TestAll threads completeHello worldHello world1Hello world2Hello world3

о/р я хочу: Console Redirection TestHello worldHello world1Hello world2Hello world3All threads complete

Что такое решение?

+3

Я подозреваю, что вы хотите использовать 'WaitAll' вместо' WhenAll'. См. Здесь http://stackoverflow.com/questions/6123406/waitall-vs-whenall – Filip

+0

Это работает так же! попробовал это – flute

+2

на самом деле, это не так. – Filip

ответ

4

Task.WhenAll возвращает Task, представляющий завершение всех этих задач в перечислимом. Вам нужно дождаться этой задачи, так как метод не блокирует поток.

Turn txtSayHello_Click в async void (который должен быть использован только для обработчиков событий) метода и ждать задач вернулись из Task.WhenAll:

async void txtSayHello_Click(object sender, EventArgs e) 
{ 
    // ... 
    await Task.WhenAll(taa); 
    // ... 
} 

Кроме того, почти всегда следует избегать использования Task конструктора. Вы должны использовать Task.Factory.StartNew с TaskScheduler.FromSynchronizationContext, если вам нужна задача для запуска в потоке пользовательского интерфейса (это зависит от того, что вы на самом деле делаете с FormConsole) или Task.Run, если вы этого не сделаете. Значение:

taa[0] = Task.Factory.StartNew(() => objclss.DoSomeThigs("Hello world"), CancellationToken.None, TaskCreationOptions.None, 
      TaskScheduler.FromCurrentSynchronizationContext()); 

Или:

taa[0] = Task.Run(() => objclss.DoSomeThigs("Hello world")); 
+2

приятная добавленная деталь i3arnon .. – flute

5

Task.WhenAll возвращает задачу, которая завершается, когда все выполняемые задачи завершены. У вас должно быть await эта задача, иначе метод продолжит выполнение.

async void txtSayHello_Click(object sender, EventArgs e) 
{ 
    ... 
    await Task.WhenAll(taa); 
    ... 
} 

Существует версия блокирование этого метода - Task.WaitAll. Он заблокирует текущий поток до тех пор, пока не будут выполнены все задачи, но не рекомендуется блокировать поток пользовательского интерфейса.

Также предпочтительным способом запуска задачи в потоке пула потоков является использование Task.Run.

+0

это отлично работает! – flute

3

Вы, скорее всего, путая WhenAll() с WaitAll(). Как уже отмечалось вы можете использовать async с await или вы можете просто использовать:

A) Task.WhenAll(taa).Wait();

B) Task.WaitAll(taa);

но в вашем случае это будет блокировать поток пользовательского интерфейса. Так что лучше положить остальную часть кода в Продолжение задачи и вызывать операции пользовательского интерфейса с Control.Invoke():

Task.WhenAll(taa).ContinueWith(t => 
{ 
    this.Invoke(() => this.txtConsole.AppendText("All threads complete")); 
}); 
+0

o/p после попытки этого (не работает в моем случае): Console Redirection TestAll threads completeHello worldHello world1Hello world2Hello world3. – flute

+0

@flute обновил мой ответ – Fabjan

+0

, используя только continueWith дает ошибку 'Неверная операция перекрестных ссылок: Control 'txtConsole' обращается из потока, отличного от потока, в котором он был создан. ', Но работает async-wait. – flute

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