调用并行化foreach的问题

时间:2010-08-12 12:52:53

标签: c# parallel-processing

我在使用System.Threading.Tasks.Parallel.ForEach时遇到问题。身体foreach progressBar想要更新。 但Invoke方法有时会冻结。

我将代码附加到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)。 试着解决我的问题。

谢谢。

2 个答案:

答案 0 :(得分:5)

您的问题是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; 
} 

答案 1 :(得分: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;