我在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;
}
答案 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...
}
}