6

在此代码中,当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();
}
4

4 回答 4

7

好吧,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 的线程持有被中止线程所需的锁,则可能发生死锁。

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

于 2010-11-15T10:29:00.973 回答
1

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

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);
        }

    }
}

但是,我会考虑使用这些指南我推荐Joseph Albahari 的中止指南- 简而言之 C# 的作者)阅读 .NET 中的线程,而不是使用这种方法,尤其是在执行 IO 或数据库时可能使对象处于意外状态的操作。

于 2010-11-15T11:24:06.163 回答
1

我建议您查看内置的同步原语,例如 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;
            }                
        }
    }
}
于 2010-11-15T11:08:07.927 回答
0

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

检查这个:

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

于 2010-11-15T10:35:43.013 回答