【问题标题】:c# - How to force closing of background worker that run it own threadsc# - 如何强制关闭运行它自己的线程的后台工作者
【发布时间】:2014-09-29 09:38:08
【问题描述】:

我有 wpf 应用程序,我们称之为A-appA-app 与后台工作人员一起运行异步 B-methodB-method 位于不同的项目中,它为 B-method init 部分创建了几个线程。

用户可以要求运行B-method,也可以要求取消运行并重新启动。 问题是,如果它在初始化时被取消,运行B-method 的后台工作程序将被取消,但线程不会。 重新启动会创建更多的线程,这些线程不能与之前运行的线程同时工作,并且会产生一些错误。

Threads 方法主要是在等待。

如何停止B-method 并取消它创建的线程?

不同的 AppDomain 是否有任何帮助? (而不是关闭整个应用程序域?)如果是,那应该怎么做? 有没有更好的办法?

更多详情:

  • B 方法在某些设备(可能很多)上运行测试。该方法的初始化是连接到设备 - i/o - 所以大部分时间都花在等待上(这就是我们决定使连接初始化并行的原因)。从 2 个不同线程连接到同一设备可能会导致问题.

【问题讨论】:

  • 是的,AppDomain 可能是最好的。如何卸载已被多次询问(请参阅此处右侧的列)
  • AppDomain 和线程的概念是正交的。卸载 AppDomain 不是取消工作人员的好方法。

标签: c# multithreading asynchronous appdomain


【解决方案1】:

我建议您根本不要创建线程,而是使用 TaskScheduler 并使用并行任务库:

http://msdn.microsoft.com/en-us/library/dd997402%28v=vs.110%29.aspx

TaskScheduler 本身是 ThreadPool 的包装器,它处理线程。它甚至可以做诸如 WorkStealing、Task Inling 之类的东西。

最好从这里开始:http://msdn.microsoft.com/en-us/library/dd997402%28v=vs.110%29.aspx

另一种方法是使用 CancallionToken 启动任务,这使您可以取消任务。见这里:http://msdn.microsoft.com/en-us/library/dd537607%28v=vs.110%29.aspx

编辑:好的,没有 TPL,阻塞线程。这基本上只剩下 Thread.Abort。 这个乱七八糟,但是没有完美的世界,所以把Form想象成Application A,ClassB就是Application B:

public partial class MainWindow : Window
{
    Thread _threadA;
    Thread _threadB;
    Thread _threadC;

    ClassB b1 = new ClassB();
    ClassB b2 = new ClassB();
    ClassB b3 = new ClassB();

    public MainWindow()
    {
        InitializeComponent();

        _threadA = new Thread(() => b1.DoSomeWork("A"));
        _threadB = new Thread(() => b2.DoSomeWork("B"));
        _threadC = new Thread(() => b3.DoSomeWork("C"));
    }

    private void btnStartWork_Click(object sender, RoutedEventArgs e)
    {
        _threadA.Start();
        _threadB.Start();
        _threadC.Start();
    }

    private void btnStopThreadA_Click(object sender, RoutedEventArgs e)
    {
        AbortThreadA();
    }

    private void btnStopThreadB_Click(object sender, RoutedEventArgs e)
    {
        AbortThreadB();
    }

    private void btnStopThreadC_Click(object sender, RoutedEventArgs e)
    {
        AbortThreadC();
    }

    private void AbortThreadA()
    {
        _threadA.Abort();
    }

    private void AbortThreadB()
    {
        _threadB.Abort();
    }

    private void AbortThreadC()
    {
        _threadC.Abort();
    }

    private void btnStopAll_Click(object sender, RoutedEventArgs e)
    {
        AbortThreadA();
        AbortThreadB();
        AbortThreadC();
    }
}



class ClassB
{
    public void DoSomeWork(string threadIdentifier)
    {
        try
        { 
            string preWorkString = "Work work Okeydokey. Thread: " + threadIdentifier;
            string postWorkString = "Job's Done. Thread: " + threadIdentifier;

            while (true)
            {
                System.Diagnostics.Debug.WriteLine(preWorkString);
                Thread.Sleep(5000);
                System.Diagnostics.Debug.WriteLine(postWorkString);
            }
        }
        catch (ThreadAbortException)
        {
            System.Diagnostics.Debug.WriteLine("Thread aborted. Thread: " + threadIdentifier);
            Thread.ResetAbort();
        }
    }
}

需要ResetAbort,否则错误会冒泡。

这是一个可能的解决方案吗?

【讨论】:

  • 由于使用旧环境,我无法升级 .Net 版本,所以这不是一个选项(我认为任务来自 .Net 4)。只是出于好奇,有没有比线程更好的取消任务的方法?它只是一个包装器,线程大多处于等待状态。
  • TaskScheduler 只是线程池的包装器。您可以让 ThreadPool 处理线程。您基本上在开始时创建大量线程并让池在它们之间进行处理。我想说直接创建新线程几乎不是一个好主意。
  • 我明白,问题仍然是关于取消的。使用池而不是直接启动线程有什么不同?取消有何不同?
  • 编辑我的帖子以获得另一个解决方案
【解决方案2】:

你是否拥有 B 方法(即你能改变它吗?)

我假设 B 方法本质上是不可取消的,而您只是取消在模块 A 中调用它的后台工作人员?

B 方法需要更改为可取消或可重入。通过重入,我的意思是它将允许对其自身进行多次调用,并将重用任何已经在进行中的现有 init 序列。

【讨论】:

  • B 方法是长期测试动作的起点(它调用许多其他方法,这里大部分时间也是 I/O 和等待)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-09
  • 2013-06-05
相关资源
最近更新 更多