Межпотоковое исключение с использованием метода WhenAll - PullRequest
0 голосов
/ 10 апреля 2019

Я изучал async / await и создал небольшой пример, который мне не помог. Я хотел изменить текстовые реквизиты параллельно. Возможно ли это?

В сообщении об ошибке говорится: «Операция между потоками недопустима: элемент управления textBox1 доступен из потока, отличного от потока, в котором он был создан».

private async void DeleteAsync()
    {
        var tasks = new List<Task>();
        var list1 = new List<Control>();
        var list2 = new List<Control>();


        list1.Add(textBox1);
        list1.Add(textBox2);

        list2.Add(textBox3);
        list2.Add(textBox4);


        tasks.Add(Task.Run(() => ChangeText(list1)));
        tasks.Add(Task.Run(() => ChangeText(list2)));

        await Task.WhenAll(tasks);

        Console.Write("enddd");
    }


    private void ChangeText(List<Control> lst)
    {            
        foreach (var ctrl in lst)
        {
            ctrl.Text = "22";
        }            
    }

Большое спасибо!

Ответы [ 2 ]

2 голосов
/ 10 апреля 2019

Я хотел изменить текстовые реквизиты параллельно. Возможно ли это?

Нет. Но вы можете внести столько изменений, сколько хотите, в поток пользовательского интерфейса, в пределах разумного.

Один из способов сделать это - , используя обычный async / await, без , используя Task.Run. Другими словами, используя асинхронность вместо параллелизма.

Если вам нужно нужен параллелизм, то одним из способов обновления пользовательского интерфейса является использование типов IProgress<T> / Progress<T> для сообщения об обновлениях прогресса. Примерно так:

var progress1 = new Progress<string>(update =>
{
  foreach (var ctrl in list1)
    ctrl.Text = update;
});
var progress2 = new Progress<string>(update =>
{
  foreach (var ctrl in list2)
    ctrl.Text = update;
});

tasks.Add(Task.Run(() => ChangeText(progress1)));
tasks.Add(Task.Run(() => ChangeText(progress2)));
await Task.WhenAll(tasks);

...

private void ChangeText(IProgress<string> progress)
{
  progress?.Report("22");
}

Одним из приятных преимуществ использования подхода IProgress<T> является то, что ваш код обработки теперь можно тестировать без пользовательского интерфейса. Т.е. вы можете написать для него модульные тесты.

1 голос
/ 10 апреля 2019

В существующем состоянии не требуется Async Await Pattern или Tasks , поскольку все, что вы делаете, - это обновление UI и * 1007. * UI являющийся однопоточный

Хотя вы предполагаете, что вы делаете что-то вроде IO Bound природы, которая может остановить ваш поток пользовательского интерфейса (например, доступ к БД, ЭСТ), вы можете сделать что-то вроде этого.

private async Task DoSomethingAsync()
{
    ...

    await DoSomtehingAwsomeAsync(list1);
    await DoSomtehingAwsomeAsync(list2);

}

...

private async Task ChangeText(List<Control> lst)
{           
    // Awesome IO bound work here

    // await CallDataBaseAsync();

    // await VisitGrandMotherAsync();

    foreach (var ctrl in lst)
    {
        ctrl.Text = "22";
    }            
}

В этом случае текущий SyncronizationContext будет передаваться в IAsyncStateMachine, создаваемый компилятором каждый раз, когда вы вызываете await, а продолжение запускается в вызывающем context (т.е. все после await). Это будет означать, что весь ваш код UI отправляется обратно в поток UI (что сведет на нет ваши исключения из нескольких потоков).

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