我有一个 .NET 3.5 上的 WinForms 应用程序。在这种形式下,用户触发在另一个线程(准确地说是 BackgroundWorker
)中执行的操作,以免阻塞 UI 线程。我在 MVP 中,所以所有这一切都是由与 View 界面交互的演示者完成的(由 Windows 窗体实现)。到目前为止一切顺利。
我想介绍一种功能,即为后台操作引入超时时间,以便在取消操作之前完成。听起来很简单。但是后台操作调用第三方组件上的单个函数可能永远不会返回,因此 BackgroundWorker
的取消功能在这里对我没有用。此外,BackgroundWorker.RunWorkerCompleted
允许我返回到 UI 线程,因此我需要等待超时或成功并能够返回到我的调用线程(即 UI 线程)。
我尝试使用一个普通的旧线程
(它支持Abort()
)和一个在第二个线程上运行的计时器,但似乎无法得到它可以正常工作,因为 Join()
阻塞了我的 UI 线程,尽管描述表明它将阻塞 "while continuing to perform standard COM and SendMessage pumping "。不可否认,我认为这意味着它会继续处理 Windows 消息,但事实并非如此。
int timeoutInMsec = 10000;
Thread connectThread = new Thread(Connect);
Thread timerThread = new Thread(() =>
{
var timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
timer.Tick += (_s, _e) =>
{
timer.Stop();
if (connectThread.ThreadState == ThreadState.Running)
connectThread.Abort();
};
};
connectThread.Start();
timerThread.Start();
timerThread.Join();
connectThread.Join();
基于 this question ,我尝试删除第二个计时器线程并添加 ManualResetEvent
并在计时器计时或 Connect 方法确实完成时调用 Set()
。在这里,我使用了 WaitOne
而不是 Join
,但不幸的是,这也阻塞了我的 UI 线程。我还找到了this other question ,这是一个 CancellationTokenSource
,遗憾的是在 .NET 3.5 中不可用。
那么,在 .NET 3.5 中,我如何启动我的工作线程并能够在给定的时间后终止它,同时又能够返回到我启动工作线程的线程执行某种 OnCompleted
处理程序?
非常感谢!
PS:我在 .NET 中的多线程编程方面没有很多经验,所以如果这是微不足道的,我很抱歉。
最佳答案
如果我正确理解你的问题,下面的算法应该可以解决你的问题:
和以前一样,创建一个 BackgroundWorker 来完成您的后台工作。
在 BackgroundWorker_DoWork 中,
- 创建一个新线程(我们称之为“第三方线程”)来调用您的第三方库,然后
- 等待第三方线程完成或超时结束。 (*)
这样,您的 UI 就不会阻塞,因为只有 Backgroundworker 线程在等待,而不是主线程。
现在谈谈有趣的部分:如何等待第三方线程完成(标有 (*) 的步骤)?
我的建议是简单地使用“循环等待 sleep ”,即(伪代码,您可以使用 Stopwatch
类作为超时):
do until (third-party thread has finished or x seconds have elapsed):
Thread.Sleep for 100ms
if third-party thread has not finished:
Abort it // we don't have another choice
else
Process the result
这不是最佳实践,但它很简单,它可以完成工作,一旦一切正常,您可以随时将其替换为花哨的跨线程同步东西(要正确实现它并非易事)。
关于c# - 在给定时间后等待并终止线程而不阻塞.NET 3.5,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42575654/