2010-08-12 30 views
3

我在使用System.Threading.Tasks.Parallel.ForEach時遇到了問題。進度條正文要更新。 但調用方法有時會凍結。調用並行化foreach的問題

我將代碼附加到prograssbar和Buton的表單中。

private void button1_Click(object sender, EventArgs e) 
{ 
    DateTime start = DateTime.Now; 
    pforeach(); 
    Text = (DateTime.Now - start).ToString(); 
} 


private void pforeach() 
{ 
    int[] intArray = new int[60]; 
    int totalcount = intArray.Length; 
    object lck = new object(); 
    System.Threading.Tasks.Parallel.ForEach<int, int>(intArray, 
    () => 0, 
    (x, loop, count) => 
    { 
     int value = 0; 
     System.Threading.Thread.Sleep(100); 
     count++; 
     value = (int)(100f/(float)totalcount * (float)count); 

     Set(value); 
     return count; 
    }, 
    (x) => 
    { 

    }); 
} 

private void Set(int i) 
{ 
    if (this.InvokeRequired) 
    { 
     var result = Invoke(new Action<int>(Set), i); 
    } 
    else 
     progressBar1.Value = i; 
} 

有時它傳遞沒有問題,但通常凍結 var result = Invoke (new Action <int> (Set), i)。 試着在問題中解決我的問題。

謝謝。

回答

4

您的問題是Invoke(和排隊Task到UI TaskScheduler)都需要在UI線程是處理其消息循環。但事實並非如此。它仍在等待Parallel.ForEach循環完成。這就是爲什麼你會看到一個僵局。

如果你想Parallel.ForEach而不阻塞UI線程中運行,它包裝成一個Task,因爲這樣的:

private TaskScheduler ui; 
private void button1_Click(object sender, EventArgs e) 
{ 
    ui = TaskScheduler.FromCurrentSynchronizationContext(); 
    DateTime start = DateTime.Now; 
    Task.Factory.StartNew(pforeach) 
     .ContinueWith(task => 
     { 
      task.Wait(); // Ensure errors are propogated to the UI thread. 
      Text = (DateTime.Now - start).ToString(); 
     }, ui); 
} 

private void pforeach() 
{ 
    int[] intArray = new int[60]; 
    int totalcount = intArray.Length; 
    object lck = new object(); 
    System.Threading.Tasks.Parallel.ForEach<int, int>(intArray, 
    () => 0, 
    (x, loop, count) => 
    { 
     int value = 0; 
     System.Threading.Thread.Sleep(100); 
     count++; 
     value = (int)(100f/(float)totalcount * (float)count); 

     Task.Factory.StartNew(
      () => Set(value), 
      CancellationToken.None, 
      TaskCreationOptions.None, 
      ui).Wait(); 
     return count; 
    }, 
    (x) => 
    { 

    }); 
} 

private void Set(int i) 
{ 
    progressBar1.Value = i; 
} 
+0

謝謝它做工精細。 – Aik 2010-08-12 14:04:13

1

我在看我是如何做到這一點,這種改變可能會幫助您:

在我的構造函數中我有這樣一行:

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

那麼我這樣做:

private void changeProgressBar() 
    { 
     (new Task(() => 
     { 
      mainProgressBar.Value++; 
      mainProgressTextField.Text = mainProgressBar.Value + " of " + mainProgressBar.Maximum; 
     })).Start(uiScheduler); 
    } 

這擺脫了需要使用Invoke,並且如果您使用Task方法,那麼它可能會解決您的問題。

我覺得這些都是在System.Threading.Tasks;