在给定时间后等待并终止线程,而不会阻塞.NET 3.5



我在.NET 3.5上有一个winforms应用程序。在此形式中,用户会触发一个操作,该操作是在另一个线程中执行的(确切的BackgroundWorker),以免阻止UI线程。我在MVP中,因此所有这些都是由与视图接口交互的主持人完成的(由Windows表单实现)。到目前为止还不错。

我想介绍功能,从而引入超时时间以供背景操作在取消之前完成。听起来很简单。但是背景操作在第三方组件上调用了一个可能永远不会返回的功能,因此BackgroundWorker的取消功能在这里对我无用。另外,BackgroundWorker.RunWorkerCompleted允许我重回UI线程,因此我需要等待超时或成功,并能够回到我的调用线程(即UI线程)。

我使用普通的旧Thread(确实支持Abort())和在第二个线程上运行的计时器尝试了一下,但是由于Join()正在阻止我的UI线程,尽管描述说明说明这一点,但似乎无法使其正常工作。它将阻止"继续执行标准com和sendmessage泵送" 。诚然,我认为这意味着它将继续处理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();

基于此问题,我尝试删除第二个计时器线程,并在计时器勾选时添加ManualResetEvent并调用Set(),或者当Connect方法确实完成时。在这里,我使用WaitOne而不是Join,但不幸的是,这也阻止了我的UI线程。我还发现了另一个问题,不幸的是,它是.NET 3.5中不可用的CancellationTokenSource

因此,我如何在.NET 3.5的给定时间后旋转我的工人并能够终止它执行一种OnCompleted处理程序?

非常感谢!

ps:我在.NET中没有太多的多线程编程经验,所以很抱歉,如果这很微不足道。

如果我正确理解您的问题,则以下算法应解决您的问题:

  • 和以前一样,创建一个背景工作者来完成您的背景工作。

  • 在BackgroundWorker_dowork中,

    • 创建一个新线程(让我们称其为"第三方线程")来调用您的第三方库,然后
    • 等待第三方线程完成或超时越过。(*)

这样,您的UI将不会阻止,因为只有背景工作人员线程在等待,而不是主线程。

现在关于有趣的部分:您如何等待第三方线程完成(标有(*)的步骤)?

我的建议是简单地使用"循环等待睡眠",即(伪代码,您可以在超时使用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

这不是最好的练习,但是很简单,它可以完成工作,您可以随时用花哨的跨线程串联的东西替换它(这是不宽容的)p>

在非GUI线程上创建Forms.Timer是没有用的。不要在单独的线程上创建它。为什么要Joining线程?Join的使用是阻止当前线程直到另一个线程完成。

这是未经测试的伪代码,这是目的。

public class Form1: Form1
{
    private int timeoutInMsec = 10000;
    private System.Windows.Forms.Timer _timer;
    private Thread _connectThread;
    public Form1()
    {
        _connectThread = new Thread(Connect);
        _connectThread.Start();
        _timer = new System.Windows.Forms.Timer() { Interval = timeoutInMsec };
        _timer.Tick += (_s, _e) => 
                {
                    _timer.Stop();
                    if (_connectThread.ThreadState == ThreadState.Running)
                        _connectThread.Abort();
                };
        };
    }
    private void Connected()
    {
    }
    private void Aborted()
    {
    }
    private void Connect()
    {
        try
        {
            DoConnect3rdPartyStuff();
            this.Invoke(Connected);
        }
        catch(ThreadAbortException)
        {
            // aborted
            this.Invoke(Aborted);
        }       
    }   
}

最新更新