How do I abort multiple threads?
Asked Answered
U

4

6

In this code, when button1 is clicked twice, it creates 2 separate threads. On one click, it creates a new thread on the heap and field t1 points to the new thread on the heap. When I click button2, it aborts the last thread (which t1 refers to).

How do I abort the other thread?

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();
}
Unification answered 15/11, 2010 at 10:26 Comment(1)
Is there any reason you cannot use BackgroundWorker or similar construct? Spawning threads involves a lot of overhead, so it makes sense to reuse them whenever possible.Taco
D
7

Well, the OO answer would be to hold a list of threads as a field.

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

And to then add the newly constructed thread to the list in the first handler.

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

Then you could iterate through each thread in the second handler, aborting each of them in turn.

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

But I think the most important point here is that there is almost never a good reason to call Thread.Abort.

From the MSDN page:

When a thread calls Abort on itself, the effect is similar to throwing an exception; the ThreadAbortException happens immediately, and the result is predictable. However, if one thread calls Abort on another thread, the abort interrupts whatever code is running. There is also a chance that a static constructor could be aborted. In rare cases, this might prevent instances of that class from being created in that application domain. In the .NET Framework versions 1.0 and 1.1, there is a chance the thread could abort while a finally block is running, in which case the finally block is aborted.

The thread that calls Abort might block if the thread that is being aborted is in a protected region of code, such as a catch block, finally block, or constrained execution region. If the thread that calls Abort holds a lock that the aborted thread requires, a deadlock can occur.

You would be much better off using some form of signalling, such as setting a ManualResetEvent that each thread will poll at perioidic intervals. Alternatively, you could use the BackgroundWorker class that has some support for task-cancellation (call CancelAsync on it, and get the worker threads to test CancellationPending periodically). If you are on .NET 4.0, you can also use the TPL.

Downstairs answered 15/11, 2010 at 10:29 Comment(2)
Good description. But I think that "foreach(var thread in threads)" may cause Exception, "lock" must be used here.Monobasic
@acoolaum: Thanks. As far as I can tell, a lock is not necessary here. Both the handlers run on the form's message loop; they can't run simultaneously on different threads.Downstairs
C
1

I would recommend you to take a look at the built in synchronization primitives such as ManualResetEvent and WaitHandle. You can ask a thread if it's running or not by trying to join the thread with Thread.Join. Aborting a thread should only be done as a last resort if the thread is unresponsive.

Here is an modified example of your code that shows how you can prevent the thread from be restarted before it's been stopped properly.

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;
            }                
        }
    }
}
Costello answered 15/11, 2010 at 11:8 Comment(1)
One problem with that is it will block the UI until it can join t1Philosophism
P
1

The others have given the long versions of the answer, however the obvious simple solution is to simply skip recreating the thread object:

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

    }
}

However, I would consider reading up on Threading in .NET using one these guides (I'd recommend Joseph Albahari's guide on aborting - the author of C# in a nutshell) rather than use this method, particularly if you're performing IO or database operations which can leave the objects in unexpected states.

Philosophism answered 15/11, 2010 at 11:24 Comment(0)
A
0

Also, have in mind that calling Abort on a Thread is evil. You should stop the thread with a boolean condition or something like that.

Check this:

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

Antagonistic answered 15/11, 2010 at 10:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.