【问题标题】:How to stop one thread when the other finishes如何在另一个线程完成时停止一个线程
【发布时间】:2015-09-29 11:32:41
【问题描述】:

我有这个代码

        Thread thread1 = new Thread(this.DoSomething1);
        Thread thread2 = new Thread(this.DoSomething2);
        thread1.Start();
        thread2.Start();

我需要第一个完成的线程立即杀死另一个线程。 请注意,线程不是运行一段时间的那种,所以我不能使用静态变量告诉线程停止。哪个线程会先结束?

【问题讨论】:

  • 没有安全的方法可以杀死线程。至于等待其中一项任务完成,直接用Task.Run,不用手动创建线程,可以用Task.WhenAny
  • 这听起来有点像XY Problem。您的线程实际上在做什么,您需要它们以这种方式运行?
  • 伙计们,感谢您的快速响应,但是: 1. 我的线程不循环。 2. 他们做的工作完全不同。

标签: c# .net multithreading winforms


【解决方案1】:

理想情况下,您希望使用取消令牌或静态变量来安全地取消线程。


如果你决定使用cancellationToken/tokenSource:

  var tokenSource = new CancellationTokenSource();
  var token = tokenSource.Token;

  ...

  static void DoSomething1or2(CancellationToken token, CancellationTokenSource tokenSource)
  {

      //Do other work here

      //Since you said neither of your tasks were looping, just put this where
      //you'd want to cancel the thread
      if(token.IsCancellationRequested)
        return; // or token.ThrowIfCancellationRequested();

      //do some more stuff here

      tokenSource.IsCancellationRequested = true;
  }

如果您的 doSomething 方法正在循环,那么您可以在每个循环的开头或结尾检查一个布尔值,以查看其他线程是否已完成。这种技术与cancelToken非常相似。

if(otherThreadIsActive && originalCondition)
   {
       //do stuff here
   }

如果您不能等待该迭代完成,或者不能使用取消令牌,那么我建议您阅读this thread on aborting。除非绝对必要,否则最好不要使用它。


最佳方式:使用取消令牌/tokenSource

下一个最佳方法:使用某种布尔值来指示何时终止

【讨论】:

  • 这是 - 在大多数情况下 - 要走的路。您应该删除中止部分,不是因为它在技术上是错误的,而是因为它对于 OP 发布的(相当微不足道的)示例不正确。我在 OP 的代码中没有看到任何异常情况可以保证中止另一个线程(你怎么知道哪个线程被中止 - 甚至不知道哪个线程先开始,也不能保证在你发出时会发生中止命令)。如果您删除 abort 部分,我会支持您的回答。
  • @xxbbcc 我会强调他真的不应该使用 thread.Abort(),但我不想删除它。更好的人知道它,而不是使用它,而不是不知道它。
  • 我同意你的观点,所以 +1
【解决方案2】:

Thread 用于您启动并且不再与之交互的“即发即弃”线程。如果您需要与线程交互,例如在它启动后停止它,或者如果您需要知道线程何时结束,您应该使用Task.Run 而不是Thread。如果您想在任务仍在运行时取消它,您需要CancellationTokenSource

var cts = new []
{
    new CancellationTokenSource(),
    new CancellationTokenSource()
};
var ts = new[]
{
    Task.Run(() => DoSomething1(cts[0].Token), cts[0].Token),
    Task.Run(() => DoSomething2(cts[1].Token), cts[1].Token)
};

var w = Task.WaitAny(ts);
for (int i = 0; i < cts.Length; i++)
{
    if (i != w) cts[i].Cancel();
}

您的DoSomethings 必须监视CancellationToken.IsCancellationRequested 才能取消并执行清理。您需要将令牌传递给Task.Run 以检测线程是否抛出异常,我没有显示。更多信息请咨询How to Cancel a Task and Its Children

【讨论】:

    【解决方案3】:

    您在这些线程中做什么非常重要。如果两者都有 for 循环,您可以通过在循环开始时或循环体中的任何位置读取共享变量来停止线程。如果它只是繁重的计算(复杂的业务流程或复杂的数学事物),那么在某事中间杀死一个线程甚至是危险的。

    【讨论】:

    • 杀死线程总是很危险。它应该始终通过响应同步原语来合作退出。
    【解决方案4】:

    我过去在处理同步线程取消时所做的是在类中有一个静态事件来启动线程。这些线程执行不同类的函数,它们都侦听要触发的这个静态事件。当此事件触发时,volatile private bool _aboutRequested 被设置为 true,这可以在旋转的线程执行类中找到。在线程执行循环期间,完成的最后一件事是检查这个退出请求的布尔值,如果为真,则优雅地退出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多