我正在学习异步/等待,并且创建了一个对我不起作用的小例子。我想以并行方式更改文本道具。有可能吗?
错误显示为“'跨线程操作无效:控制'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";
}
}
非常感谢!
答案 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 线程中(这将消除您的跨线程异常)。