gpt4 book ai didi

c# - 在用户界面和控制台应用程序中使用 Task.Yield() 的区别

转载 作者:行者123 更新时间:2023-11-30 22:57:04 25 4
gpt4 key购买 nike

我试图在实际应用程序运行时异步显示一个表示应用程序正在运行的进度表。

如下this question ,我有以下内容:

主窗体:

public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}

async Task<int> LoadDataAsync()
{
await Task.Delay(2000);
return 42;
}

private async void Run_Click(object sender, EventArgs e)
{
var runningForm = new RunningForm();

runningForm.ShowRunning();

var progressFormTask = runningForm.ShowDialogAsync();

var data = await LoadDataAsync();

runningForm.Close();
await progressFormTask;

MessageBox.Show(data.ToString());
}
}

进度表

public partial class RunningForm : Form
{
private readonly SynchronizationContext synchronizationContext;

public RunningForm()
{
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
}

public async void ShowRunning()
{
this.RunningLabel.Text = "Running";
int dots = 0;

await Task.Run(() =>
{
while (true)
{
UpadateUi($"Running{new string('.', dots)}");

Thread.Sleep(300);

dots = (dots == 3) ? 0 : dots + 1;
}
});
}

public void UpadateUi(string text)
{
synchronizationContext.Post(
new SendOrPostCallback(o =>
{
this.RunningLabel.Text = text;
}),
text);
}

public void CloseThread()
{
synchronizationContext.Post(
new SendOrPostCallback(o =>
{
this.Close();
}),
null);
}
}

internal static class DialogExt
{
public static async Task<DialogResult> ShowDialogAsync(this Form form)
{
await Task.Yield();
if (form.IsDisposed)
{
return DialogResult.OK;
}
return form.ShowDialog();
}
}

上面的工作正常,但是当我从另一个外部调用时它不起作用。这是我的控制台应用程序:

class Program
{
static void Main(string[] args)
{
new Test().Run();
Console.ReadLine();
}
}

class Test
{
private RunningForm runningForm;

public async void Run()
{
var runningForm = new RunningForm();

runningForm.ShowRunning();

var progressFormTask = runningForm.ShowDialogAsync();

var data = await LoadDataAsync();

runningForm.CloseThread();

await progressFormTask;

MessageBox.Show(data.ToString());
}

async Task<int> LoadDataAsync()
{
await Task.Delay(2000);
return 42;
}
}

观察调试器发生的情况,进程进入 await Task.Yield() 并且永远不会进展到 return form.ShowDialog(),因此您永远看不到运行形式。然后该进程转到 LoadDataAsync() 并在 await Task.Delay(2000) 上永远挂起。

为什么会这样?它与 Task 的优先级排序方式(即:Task.Yield())有关吗?

最佳答案

Watching what happens with the debugger, the process gets to await Task.Yield() and never progresses to return form.ShowDialog() and thus you never see the RunningForm. The process then goes to LoadDataAsync() and hangs forever on await Task.Delay(2000).

Why is this happening?

这里发生的是,当您在没有任何同步上下文的控制台线程上执行 var runningForm = new RunningForm() 时(System.Threading.SynchronizationContext.Current 为 null ), 它隐式地创建了一个 WindowsFormsSynchronizationContext 的实例并将它安装在当前线程上,更多关于这个 here .

然后,当您点击 await Task.Yield() 时,ShowDialogAsync 方法返回给调用者并且 await 继续发送到那个新的同步上下文。但是,continuation 永远不会有机会被调用,因为当前线程不运行消息循环并且不会发送已发布的消息。没有死锁,但 await Task.Yield() 之后的代码从未执行过,因此甚至不会显示对话框。 await Task.Delay(2000) 也是如此。

I'm more interested in learning why it works for WinForms and not for Console Applications.

您的控制台应用程序中需要一个带有消息循环的 UI 线程。尝试像这样重构您的控制台应用程序:

public void Run()
{
var runningForm = new RunningForm();
runningForm.Loaded += async delegate
{
runningForm.ShowRunning();

var progressFormTask = runningForm.ShowDialogAsync();

var data = await LoadDataAsync();

runningForm.Close();

await progressFormTask;

MessageBox.Show(data.ToString());
};
System.Windows.Forms.Application.Run(runningForm);
}

在这里,Application.Run 的工作是启动模态消息循环(并在当前线程上安装 WindowsFormsSynchronizationContext)然后显示表单。 runningForm.Loaded 异步事件处理程序在该同步上下文中被调用,因此其中的逻辑应该按预期工作。

然而,这使 Test.Run 成为一种同步方法,即。即,它仅在表单关闭且消息循环结束时返回。如果这不是你想要的,你必须创建一个单独的线程来运行你的消息循环,就像我做的一样 with MessageLoopApartment here .

也就是说,在典型的 WinForms 或 WPF 应用程序中,您几乎不需要辅助 UI 线程。

关于c# - 在用户界面和控制台应用程序中使用 Task.Yield() 的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53874468/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com