当worker无法检查终止字符串时如何终止线程

时间:2014-05-23 10:30:49

标签: c# multithreading winforms

我在Windows窗体中运行以下代码。它调用的方法大约需要40秒才能完成,我需要让用户能够点击“中止”。按钮停止线程运行。

通常我会使用Worker()方法进行轮询,以查看_terminationMessage是否设置为" Stop"但我不能在这里这样做,因为长时间运行的方法,ThisMethodMightReturnSomethingAndICantChangeIt()是我无法控制的。

我该如何实现此用户功能?

这是我的线程代码。

        private const string TerminationValue = "Stop";
        private volatile string _terminationMessage;


        private bool RunThread()
        {

            try 
            {
              var worker = new Thread(Worker);
              _terminationMessage = "carry on";
              _successful = false;

              worker.Start();
              worker.Join();

             finally
            {
              return _successful;
            }

        }

        private void Worker()
        {
            ThisMethodMightReturnSomethingAndICantChangeIt();
            _successful = true;
        }

2 个答案:

答案 0 :(得分:2)

嗯,简单的答案是“你不能”。没有真正的线程中止可以用来取消正在发生的任何处理。

Thread.Abort将允许您中止托管线程,此时运行托管代码,但这真的只是一个坏主意。只是因为你刚刚运行单例构造函数或其他东西,很容易最终处于不一致状态。最后,你很有可能会把事情搞砸。

与问题有点正交,但为什么你仍然使用这样的线程代码?编写多线程代码非常困难,因此您希望尽可能多地使用高级功能。在你的一小段代码中很容易看到复杂性 - 你Join新创建的线程,这意味着你基本上没有从启动Worker方法中获得任何好处。新线程 - 你启动它,然后你就等了。这就像直接调用Worker一样,除非你保存一个不必要的线程。

try不会捕获在单独的线程中弹出的异常。因此,在Worker内抛出的任何异常都会导致整个进程无效。不好。

实施可靠取消的唯一方法是通过合作中止。自4.0以来,.NET为此提供了很好的构造,CancellationToken。它易于使用,它是线程安全的(与您的解决方案不同),它可以通过所有方法链传播,以便您可以在深度实现取消。可悲的是,如果你只是无法修改ThisMethodMightReturnSomethingAndICantChangeIt方法,那你就不走运了。

唯一有效的“支持”“取消”模式是Process.Kill。您必须在一个单独的进程中启动处理方法,而不仅仅是一个单独的线程。这可能会被杀死,也不会伤害你自己的进程。当然,这意味着你必须把这个调用分成一个新的过程 - 这通常很棘手,而且它不是一个非常好的设计(尽管你似乎没什么选择)。

因此,如果该方法不支持某种形式的取消,那么就这样对待它。它不能流产,期间。任何中止它的方式都是肮脏的黑客。

答案 1 :(得分:0)

嗯,到目前为止,这是我的解决方案。我建议你一定会阅读更新的.NET更高级功能。感谢正确方向的指针

    private void RunThread()
    {
        try
        {
            var worker = new Thread(Worker);

            SetFormEnabledStatus(false);
            _usuccessful = false;

            worker.Start();

            // give up if no response before timeout
            worker.Join(60000);      // TODO - Add timeout to config
            worker.Abort();
        }
        finally
        {
            SetFormEnabledStatus(true);
        }
    }

    private void Worker()
    {
        try
        {
            _successful= false;
            ThisMethodMightReturnSomethingAndICantChangeIt();
            _successful = true;
        }
        catch (ThreadAbortException ex)
        {
            // nlog.....
        }
        catch (Exception ex)
        {
            // nlog...
        }

    }