需要一种让前端在不停止系统消息流的情况下等待漫长过程完成的方法
本文关键字:等待 情况下 方法 过程 系统消息 不停止 一种 前端 | 更新日期: 2023-09-27 18:22:50
我正在使用C#和.NET 4.0。
我的程序由前端和后端组成。后端将作为Windows服务运行,该服务在系统启动时自动启动。前端是一个WPF应用程序,用户将在准备好使用该应用程序时启动它。
作为启动过程的一部分,前端需要与后端建立通信。这是使用XMPP完成的,并且运行良好。执行通信的代码有一个BeginStart方法,该方法返回一个WaitHandle。在try-catch中,我调用BeginStart,获取WaitHandle,并调用WaitHandle的WaitOne(TimeSpan)方法。请注意,程序此时正在显示一个启动屏幕。这些调用是在前端线程上进行的。
虽然这很好,但我发现在运行WaitOne调用时点击其他窗口没有效果。从本质上讲,所有Windows消息通信都将暂停,等待此WaitHandle返回。
我认为我需要在另一个线程中执行BeginStart和WaitOne调用;我仍然需要前端等待WaitOne返回,因为程序应该会死,除非WaitOne调用成功返回(没有超时),否则不会显示任何用户界面。它只需要让其他程序处理他们的Windows消息。
我该如何做到这一点?WPF中没有Application.DoEvents方法。
Tony
编辑:
如果我提供更多的信息,看起来可能会有所帮助。
我的WPF应用程序是这个产品的用户界面,只占整个产品的一半。另一半作为Windows服务在同一台电脑上运行,我们称之为后端。后端发生的事情并不重要,只是它正在使用XMPP侦听到它的连接。
当用户界面(或前端)启动时,它必须做的一件事就是使用XMPP与后端建立通信。这通常只需要一两秒钟的时间。但是,当用户启动前端时,可能出现了问题,或者后端可能出现故障。我不希望前端无限期地等待;它需要在一段合理的时间后超时,并显示错误消息并死亡。
为了建立通信,我启动了一个作为单独线程运行的模块。我正在调用它的BeginStart方法,该方法返回一个WaitHandle。然后,我在WaitHandle上等待,将TimeSpan设置为我的超时时间。以下是片段:
try {
WaitHandle handle = BackgroundProcess.BeginStart();
if ( !handle.WaitOne( Timeout ) ) {
// Something went wrong; Notify user here
return false;
}
// Make sure that the Background Process has started all of it's internal modules
bool allAreRunning;
Stopwatch timer = new Stopwatch();
timer.Reset(); timer.Start();
do {
allAreRunning = true;
foreach ( Module module in BackgroundProcess.Modules ) {
if ( module.State != Module.States.Running ) {
allAreRunning = false;
break;
}
}
if ( !allAreRunning ) {
// I hate this loop, but I can't think of a better way to do this
Thread.Sleep( 1000 );
}
} while ( !allAreRunning && timer.ElapsedMilliseconds <= Timeout.TotalMilliseconds );
timer.Stop();
if ( !allAreRunning ) {
// Something went wrong; Notify user here
return false;
}
// Other initialization done here
} catch ( Exception ex ) {
// Something went wrong; Notify user here
return false;
}
为了测试这一点,我在后台关闭时在调试器中运行了该程序。我可以点击任何打开的窗口,切换到它并与它进行良好的交互。我不能做的就是点击桌面。WaitHandle.WaitOne()调用返回得非常快,因为它只在后台进程开始运行后返回,而不等待建立连接。这是一个do-wile循环,它在大部分时间段都在运行。
我需要重写这个,这样用户不仅可以切换到打开程序,还可以点击桌面。我只是不知道如何让它发挥作用。
Tony
您可以将调用移动到BackgroundWorker中,并在完成加载时捕获WorkerCompleted事件。您还可以通过ProgressChanged事件更新UI上的进度。
我找到了一种更好的方法来编码这个启动逻辑。然而,代码依赖于我们内部代码的体系结构。
我能够删除代码片段中的do-while循环,并用更好的代码替换它。现在,代码在所有Module对象上循环,并查询它们的运行状态WaitHandle。这些将添加到列表集合中。循环完成后,我通过调用.ToArray()将List转换为数组,然后调用WaitHandle.WaitAll方法。
代码现在看起来像这样:
try {
WaitHandle handle = BackgroundProcess.BeginStart();
if ( !handle.WaitOne( Timeout ) ) {
. . .
return false;
}
List<WaitHandle> waitHandles = new List<WaitHandle>();
foreach ( Module module in BackgroundProcess.Transit.SubModules ) {
WaitHandle waitHandle = module.GetWaitHandleForState( Module.States.Running );
waitHandles.Add( waitHandle );
}
if ( !WaitHandle.WaitAll( waitHandles.ToArray(), Timeout ) ) {
. . .
return false;
}
// If we get here, everything is communicating properly.
. . .
} catch ( Exception ex ) {
. . .
return false;
}
return true;
感谢
Tony