【发布时间】:2022-01-20 02:07:18
【问题描述】:
我想在我的 WinForms 应用程序上创建一个启动画面。我创建启动画面表单并运行初始化操作。
public async Task RunApplication()
{
splash = new SplashWindow();
splash.Show();
await Task.Run(InitializeAsync);
Application.Run(new frmMain());
}
//the InitializeAsync function
private async Task InitializeAsync()
{
splash.Status = "Test";
}
//the status property
public string Status
{
get { return status; }
set {
if (status != value)
{
status = value;
lblStatus.InvokeIfRequired(() => lblStatus.Text = value);
}
}
}
public static void InvokeIfRequired(this ISynchronizeInvoke snc,
MethodInvoker action)
{
if (snc.InvokeRequired) {
snc.Invoke(action, null);
} else {
action();
}
}
如您所见,我希望能够将标签更改为当前状态。但是,当我运行这段代码时,只要我点击snc.Invoke(action, null); 行,程序就会挂起。我做了一些研究/调试,似乎当我执行Task.Run 时,它会阻塞 UI 线程,并且由于在该线程上创建了启动画面,因此程序永远无法在 UI 线程上运行该操作。
我最终做的是等到任务完成并不断执行应用程序事件。
var task = Task.Run(InitializeAsync);
while(!task.IsCompleted)
{
Application.DoEvents();
Thread.Sleep(10);
}
Applicaiton.Run(new frmMain());
这行得通。但是,我想知道是否有更优雅的解决方案。
更新:
根据 Stephen Cleary 的回答,我已将代码更改为此,但它不起作用。我怎样才能让它发挥作用?
//on Program.cs file:
[STAThread]
static async Task Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
await Bootstrapper.RunApplicationAsync();
}
//on Bootstrapper.cs class
private static SplashWindow splash;
public static async Task RunApplicationAsync()
{
splash = new SplashWindow();
splash.Show();
await InitializeAsync();
Applicaiton.Run(new frmMain());
}
private static async Task InitializeAsync()
{
splash.Status = "Start";
await Task.Delay(1000); //program stops here
splash.Status = "Stop";
}
//on the SplashWindow.cs file
public string Status
{
get => lblStatus.Text;
set => lblStatus.Text = value;
}
【问题讨论】:
-
永远不要打电话给
Application.DoEvents()。它位于向后兼容 VB6 代码的框架中。它可以轻松创建几乎无法调试的重入代码问题。 -
@Enigmativity 我知道这就是我问这个问题的原因。感觉这不是一个优雅的解决方案,所以我正在寻找一种方法来摆脱它。
标签: c# multithreading winforms async-await