繁体   English   中英

如何中止多个线程?

[英]How do I abort multiple threads?

在此代码中,当单击button1两次时,它会创建2个单独的线程。 只需单击一下,它就会在堆上创建一个新线程,而字段t1指向堆上的新线程。 当我单击button2 ,它将中止最后一个线程( t1指的是)。

我如何中止其他线程?

Thread t1;
ThreadStart ts1;

private void button1_Click(object sender, EventArgs e)
{
    ts1 = new ThreadStart(myfunc);
    t1 = new Thread(ts1);
    t1.Start();
}

private void button2_Click(object sender, EventArgs e)
{
    t1.Abort();
}

好吧,OO的答案是将一个线程列表作为一个字段。

private readonly List<Thread> threads = new List<Thread>();

然后将新构造的线程添加到第一个处理程序的列表中。

var thread = new Thread(myfunc);
thread.Start();
threads.Add(thread);

然后你可以迭代第二个处理程序中的每个线程,依次中止每个线程。

foreach(var thread in threads)
   thread.Abort();

但我认为这里最重要的一点是, 几乎没有理由调用Thread.Abort

MSDN页面

当一个线程自身调用Abort时,效果类似于抛出异常; ThreadAbortException立即发生,结果是可预测的。 但是,如果一个线程在另一个线程上调用Abort,则中止将中断正在运行的任何代码。 静态构造函数也有可能被中止。 在极少数情况下,这可能会阻止在该应用程序域中创建该类的实例。 在.NET Framework 1.0和1.1版本中,当finally块正在运行时,线程可能会中止,在这种情况下,finally块将被中止。

如果正在中止的线程位于受保护的代码区域(例如catch块,finally块或约束执行区域),则调用Abort的线程可能会阻塞。 如果调用Abort的线程持有中止线程所需的锁,则可能发生死锁。

使用某种形式的信令会更好,例如设置每个线程将以perioidic间隔轮询的ManualResetEvent 或者,您可以使用对任务取消有一定支持的BackgroundWorker类(对其调用CancelAsync ,并让工作线程定期测试CancellationPending )。 如果您使用的是.NET 4.0,则还可以使用TPL

我建议你看看内置的同步原语,如ManualResetEvent和WaitHandle。 通过尝试使用Thread.Join加入线程,您可以询问线程是否正在运行。 如果线程没有响应,则只应作为最后的手段中止线程。

以下是代码的修改示例,其中显示了如何在正确停止线程之前阻止线程重新启动。

public partial class MainForm : Form
{
    private Thread t1;
    private ThreadStart ts1;
    private ManualResetEvent t1resetEvent;

    public MainForm()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // Got a thread?
        if (t1 != null) {                
            if (!t1.Join(0)) {
                // The thread seems to be running.
                // You have to stop the thread first.
                return;
            }
        }

        t1resetEvent = new ManualResetEvent(false);
        ts1 = new ThreadStart(MyFunc);
        t1 = new Thread(ts1);
        t1.Start();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        // Got a thread?
        if (t1 != null)
        {
            // Set the reset event so the thread
            // knows it's time to stop.
            t1resetEvent.Set();

            // Give the thread four seconds to stop.
            if (!t1.Join(4000)) {
                // It did not stop, so abort it. 
                t1.Abort();
            }
        }
    }

    private void MyFunc()
    {
        // Long running operation...
        while (true)
        {
            // Do someone want us to exit?
            if (t1resetEvent.WaitOne(0)) {
                return;
            }                
        }
    }
}

其他人给出了答案的长版本,但显而易见的简单解决方案是简单地跳过重新创建线程对象:

public partial class Form1 : Form
{
    Thread thread1;
    ThreadStart threadStart1;

    public Form1()
    {
        InitializeComponent();

        threadStart1 = new ThreadStart(threadTarget);
        thread1 = new Thread(threadStart1);
        thread1.Name = "Button1 thread";    
    }

    private void button1_Click(object sender, EventArgs e)
    {
        thread1.Start();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        thread1.Abort();
    }

    private void threadTarget()
    {
        Console.WriteLine(Thread.CurrentThread.Name);
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(500);
        }

    }
}

但是,我会考虑使用这些指南阅读.NET中的线程( 我推荐Joseph Albahari关于流产的指南 - 简而言之是C#的作者 )而不是使用这种方法,特别是如果你正在执行IO或数据库可以使对象处于意外状态的操作。

另外,请记住,在线程上调用Abort是邪恶的。 你应该用布尔条件或类似的东西来停止线程。

检查一下:

http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM