使用WhenAll方法的跨线程异常

时间:2019-04-10 03:40:46

标签: c# .net multithreading 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 个答案:

答案 0 :(得分:2)

  

我想以并行方式更改文本道具。有可能吗?

不。但是您可以在合理范围内封送想要对UI线程进行的所有更改。

一种执行此操作的方法是using plain async/await, without using Task.Run。换句话说,使用异步代替并行。

如果您需要并行处理,则更新UI的一种方法是使用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>方法的一个很好的好处是,现在可以在没有UI的情况下测试您的处理代码。也就是说,您可以为此编写单元测试。

答案 1 :(得分:1)

按现状,不需要异步等待模式任务,因为您要做的只是更新 UI UI 单线程

尽管假设您执行的是 IO Bound 性质的操作,很可能会使您的 UI线程停滞(例如访问db等),但是您可以执行类似的操作这个。

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 < / em>(即await之后的所有内容)。这意味着您所有的 UI 代码都将回发到 UI 线程中(这将消除您的跨线程异常)。