我一直在尝试创建一个写入数据库的任务,而不会阻塞UI线程。我遇到的最大问题是等待该过程完成而不会发生阻塞。
我一直在努力避免使用DoEvents
(虽然现在通过此程序经常使用它,但我希望在向前移动时不再使用它。)
我尝试创建在第二个线程上运行的进程,并等待它完成以及使用BackgroundWorker
。
我遇到的问题不是让代码在不同的线程中运行,而是试图找到一种等待它完成的方法。
基本上,现在我做以下事情:
BackgroundWorker
所以我可以使用ReportProgress
BackgroundWorker
BackgroundWorker
完成。对于线程,我等待IsAlive
变为false,对于BackgroundWorker
,我切换一个布尔变量。问题出在#4。
执行一个没有代码的while循环,或Thread.Sleep(0)
使UI被阻止(Thread.Sleep(0)
使程序也占用100%的程序资源)
所以我这样做:
while (!thread.IsAlive)
Thread.Sleep(1);
-OR -
while (bProcessIsRunning)
Thread.Sleep(1);
阻止了用户界面。
如果我在那里调用Application.DoEvents()
,UI会更新(虽然它是可点击的,所以我必须在此过程运行时禁用整个表单。)
如果我同步运行该进程,我仍然需要创建某种方式来更新UI(在我看来,DoEvents
调用)所以它似乎没有被锁定。
我做错了什么?
答案 0 :(得分:5)
首先,您为什么要避免DoEvents()
?
其次,您使用了冲突的条款。
等待==阻止
您说不想要阻止UI线程,但您执行想要等待任务完成。这些是相互排斥的状态。如果你等待完成某件事,你就会阻止你的线程。
如果您希望用户界面实际上可用(未阻止),那么您就不会等待完成任务。只需注册一个事件处理程序即可在其完成时触发。例如,使用BackgroundWorker处理RunWorkerCompleted事件。对于任务,您可以使用continuation将回调分派给主线程。
但似乎你只是想要更新UI,而不是可用。通常只有在您希望进度条或其他UI动画继续移动时才有意义。在这种情况下,我会打开一个模态对话框,启动我的任务,然后等待它,是的,调用DoEvents()。
var dialog = new MessageBoxFormWithNoButtons("Please wait while I flip the jiggamawizzer");
dialog.Shown += (_, __) =>
{
var task = Task.Factory.StartNew(() => WriteToDatabase(), TaskCreationOptions.LongRunning);
while (!task.Wait(50)) // wait for 50 milliseconds (make shorter for smoother UI animation)
Application.DoEvents(); // allow UI to look alive
dialog.Close();
}
dialog.ShowDialog();
模态对话框阻止用户做任何事情,但任何动画仍然有效,因为DoEvents()被调用20次(或更多)。
(您可能希望为不同的任务完成状态添加特殊处理,但这不是主题。)
答案 1 :(得分:4)
C#使用事件模型 - 您要做的是调度执行工作的进程,然后让该进程在完成时触发自定义事件或使用其中一个线程事件。当进程在“后台”释放控制中运行时,从代码中返回到系统。
答案 2 :(得分:0)
你不能在UI线程上等待。
而是向Exited
event添加处理程序。
答案 3 :(得分:0)
我不知道这是否会简化您的问题,但我们使用Essential Objects控件:http://www.essentialobjects.com/Products/EOWeb/来管理我们的长流程。
答案 4 :(得分:0)
如果将long db操作委托给后台工作线程,那么 通过从worker触发ProgressChangedEvent来指示进度,您可以处理更新UI中的进度条。 通过触发RunWorkerCompleteEvent发出相同的完成信号。
不需要轮询/循环执行所需的事件。
问题是你的后台主题正在做一些你不允许在表单中做的事情。
关闭它,更改编辑框?等等 通过某种状态机完成的操作很简单,就像在关闭线程时禁用按钮然后在RunWorkerCompleted事件中再次启用它一样简单。 你可以单独留下buutom,并在它的Click处理程序中检查一个名为busy的布尔值。
您是在卸载流程以显示进度还是仅仅是为了避免“Windiows没有响应”,或者是否存在用户在中间过程中可以理解的其他事情,例如关闭表单,取消操作等。
一旦你弄清楚UI应该做什么,你就可以将背景工程事件挂钩到一些管理事物的代码中。