Проблема с Invoke для распараллеливания foreach - PullRequest
4 голосов
/ 12 августа 2010

У меня проблема с использованием 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 ]

5 голосов
/ 12 августа 2010

Ваша проблема в том, что Invoke (и постановка в очередь Task для пользовательского интерфейса TaskScheduler) требуют, чтобы поток пользовательского интерфейса обрабатывал свой цикл сообщений. Однако это не так. Он все еще ожидает завершения цикла Parallel.ForEach. Вот почему вы видите тупик.

Если вы хотите, чтобы Parallel.ForEach работал без блокировки потока пользовательского интерфейса, оберните его в 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 голос
/ 12 августа 2010

Я смотрел, как я это сделал, и это изменение может вам помочь:

В моем конструкторе есть эта строка:

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;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...