C#WPF线程如何正确停止

时间:2011-06-14 18:42:11

标签: c# wpf multithreading backgroundworker

所以我在过去几个月一直在玩线程,虽然我的输出是预期的,但我有一种感觉,我不是这样做的最佳方式。我似乎无法从任何与我合作的人那里得到一个直接的答案,我认为我会问你们。

问题:我会试着让这个简单,所以请耐心等待。假设我有一个具有开始和停止按钮的表单。触发启动按钮和启动线程的事件。在这个帖子的DoWork里面,它会调用3个方法。 Method1()打印到控制台“A \ n”10次,中间暂停10秒。 Method2()和Method3()完全相同,只是在Console.WriteLine之间的不同字母和不同的暂停时间。现在,当您按下停止按钮时,您希望立即响应。我不想等待方法完成。我该怎么做?

我这样做的方法是将我的BackgroundWorker传递给每个方法并检查worker.CancellationPending就像这样

 public void Method1(BackgroundWorker worker)
    {
      for(int i = 0; i < 10 && !worker.CancellationPending; ++i)
      {
        Console.WriteLine("A");
        for(int j = 0; j < 100 && !worker.CancellationPending; ++i)
        {
          Thread.Sleep(100);
        }
      }
    }

就像我说的那样给了我想要的结果然而想象一下,method1变得复杂得多,假设它使用的是一个带有keydown和key的DLL。如果我只是中止线程,我可能会让自己处于不受欢迎的状态。我发现自己用!worker.CancellationPending乱丢我的代码。实际上每个代码块都在检查CancellationPending。我在网上看了很多例子,我很少看到人们像我一样传递一个线程。这方面的最佳做法是什么?

3 个答案:

答案 0 :(得分:1)

考虑使用迭代器(yield return)来分解步骤。

  public void Method1(Backgroundworker worker)
  {
     foreach (var discard in Method1Steps)
     {
        if (worker.CancelationPending)
           return;
     }
  }

  private IEnumerable<object> Method1Steps()
  {
     for (int i = 0; i < 10; ++i)
     {
        yield return null;
        Console.WriteLine("A");
        for (int j = 0; j < 100; ++i)
        {
           Thread.Sleep(100);
           yield return null;
        }
     }
  }

如果你有一堆try / catch / finally或一堆方法调用需要了解取消,这个解决方案可能更难实现。

答案 1 :(得分:0)

是的,你正确地做到了。起初看起来可能很尴尬,但它确实是最好的选择。它绝对比中止一个线程要好得多。正如您所发现的,循环迭代是检查CancelationPending的理想候选者。这是因为循环迭代通常会隔离逻辑工作单元,从而轻松描绘安全点。安全点是执行线程的标记,可以在不破坏任何数据的情况下轻松完成终止。

诀窍是经常在安全点轮询CancelationPending,以便及时向呼叫者反馈取消成功完成,但不要太频繁地对性能产生负面影响或“乱丢代码”。

在您的特定情况下,内部循环是轮询CancelationPending的最佳位置。我会省略对外循环的检查。原因是因为内循环是大部分时间花费的地方。对外循环的检查是没有意义的,因为外循环除了使内循环运行外几乎没有实际工作。

现在,在GUI端,您可能希望将停止按钮变灰,以便让用户知道取消请求已被接受。您可以显示“取消等待”等消息以使其清晰。一旦您收到取消已完成的反馈,您就可以删除该消息。

答案 2 :(得分:0)

好吧,如果您处于必须中止CPU密集型线程的情况下,那么您在一个循环或另一个循环中测试“Abort”布尔值(或取消令牌)时会感到困惑(可能不是最里面的 - 取决于这需要多长时间)。 AFAIK,您可以从内循环“返回”,因此退出方法 - 无需在每个级别进行检查!为了最大限度地减少开销,请尝试将其设置为local-ish布尔值,即尽量不要通过半打......类来取消引用它。

Maybee继承了'Stoppable'中的类,它有'Abort'方法和'Stop'布尔值?上面的示例线程花费大部分时间休眠,因此在检查任何内容之前,您将获得50ms的平均延迟。在这种情况下,您可以等待某个事件超时而不是休眠。覆盖'Abort'以设置事件以及调用继承的Abort&amp;所以尽早终止等待。如果你实现Dan所描述的这个新功能,你也可以在cancellationToken委托/回调中设置事件。

实际上很少有Windows API等不容易“不可操作”或没有异步“Ex”版本,所以它是错误的......“几乎”总是可以取消,无论如何,例如。关闭套接字以强制读取套接字除外,写入临时文件以强制文件夹更改通知返回。

RGDS, 马丁