Wou*_*ort 23 c# winforms task-parallel-library async-await
我在WinForms应用程序中有以下代码,只有一个按钮和一个标签:
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
await Run();
}
private async Task Run()
{
await Task.Run(async () => {
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我正在研究的实际应用程序的简化版本.我的印象是,通过在我使用async/await Task.Run我可以设置label1.Text属性.但是,当运行此代码时,我得到错误,我不在UI线程上,我无法访问该控件.
为什么我无法访问标签控件?
svi*_*ick 27
当你使用时Task.Run(),你不希望代码在当前上下文中运行,所以这正是发生的事情.
但是不需要Task.Run()在代码中使用.正确编写的async方法不会阻塞当前线程,因此您可以直接在UI线程中使用它们.如果这样做,await将确保该方法在UI线程上恢复.
这意味着如果您编写这样的代码,它将起作用:
private async void button1_Click(object sender, EventArgs e)
{
await Run();
}
private async Task Run()
{
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
}
Run Code Online (Sandbox Code Playgroud)
Sri*_*vel 14
试试这个
private async Task Run()
{
await Task.Run(async () => {
await File.AppendText("temp.dat").WriteAsync("a");
});
label1.Text = "test";
}
Run Code Online (Sandbox Code Playgroud)
要么
private async Task Run()
{
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
}
Run Code Online (Sandbox Code Playgroud)
要么
private async Task Run()
{
var task = Task.Run(async () => {
await File.AppendText("temp.dat").WriteAsync("a");
});
var continuation = task.ContinueWith(antecedent=> label1.Text = "test",TaskScheduler.FromCurrentSynchronizationContext());
await task;//I think await here is redundant
}
Run Code Online (Sandbox Code Playgroud)
async/await不保证它将在UI线程中运行.await将捕获当前SynchronizationContext并在任务完成后继续执行捕获的上下文.
所以你的情况,你有一个嵌套await这里面Task.Run,因此第二await将捕获它不会是上下文UiSynchronizationContext,因为它正在被执行WorkerThread的ThreadPool.
这是否回答了你的问题?
Met*_*tro 11
试试这个:
更换
label1.Text = "test";
Run Code Online (Sandbox Code Playgroud)
同
SetLabel1Text("test");
Run Code Online (Sandbox Code Playgroud)
并将以下内容添加到您的类:
private void SetLabel1Text(string text)
{
if (InvokeRequired)
{
Invoke((Action<string>)SetLabel1Text, text);
return;
}
label1.Text = text;
}
Run Code Online (Sandbox Code Playgroud)
如果您不在UI线程上,则InvokeRequired返回true.Invoke()方法获取委托和参数,切换到UI线程,然后递归调用该方法.在Invoke()调用之后返回,因为在Invoke()返回之前已经递归调用了该方法.如果在调用方法时恰好位于UI线程上,则InvokeRequired为false,并直接执行赋值.
为什么使用Task.Run?启动一个新的工作线程(cpu绑定),它会导致你的问题.
你应该这样做:
private async Task Run()
{
await File.AppendText("temp.dat").WriteAsync("a");
label1.Text = "test";
}
Run Code Online (Sandbox Code Playgroud)
等待确保您将继续使用相同的上下文,除非您使用.ConfigureAwait(false);
因为它位于不同的线程上,并且不允许跨线程调用。
您需要将“上下文”传递给您正在启动的线程。请参阅此处的示例:http ://reedcopsey.com/2009/11/17/synchronizing-net-4-tasks-with-the-ui-thread/