Как прервать несколько потоков? - PullRequest
5 голосов
/ 15 ноября 2010

В этом коде, если дважды щелкнуть button1, создается 2 отдельных потока.Одним щелчком мыши создается новый поток в куче, а поле t1 указывает на новый поток в куче.Когда я нажимаю button2, он прерывает последний поток (к которому относится t1).

Как прервать поток other ?

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 ]

7 голосов
/ 15 ноября 2010

Что ж, 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 прерван.

Поток, вызывающий Abort, может заблокироваться, если прерываемый поток находится в защищенной области кода, такой как блок перехвата, блок finally или область ограниченного выполнения.,Если поток, вызывающий Abort, удерживает блокировку, необходимую для прерванного потока, может произойти тупик.

Было бы намного лучше использовать некоторую форму сигнализации, например, настройкуManualResetEvent, что каждый поток будет опрашивать с периодической периодичностью.В качестве альтернативы вы могли бы использовать класс BackgroundWorker, который имеет некоторую поддержку отмены задач (вызовите CancelAsync для него и попросите рабочие потоки периодически проверять CancellationPending).Если вы используете .NET 4.0, вы также можете использовать TPL .

1 голос
/ 15 ноября 2010

Другие дали длинные версии ответа, однако очевидное простое решение - просто пропустить воссоздание объекта потока:

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

    }
}

Тем не менее, я хотел бы прочитать о Threading в .NET , используя одно из этих руководств ( Я бы порекомендовал Руководство Джозефа Албахари по прерыванию - автор C # в вкратце ), а не использовать этот метод, особенно если вы выполняете операции ввода-вывода или базы данных, которые могут оставить объекты в неожиданных состояниях.

1 голос
/ 15 ноября 2010

Я бы рекомендовал вам взглянуть на встроенные примитивы синхронизации, такие как 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;
            }                
        }
    }
}
0 голосов
/ 15 ноября 2010

Также имейте в виду, что призыв к прерыванию в потоке - это зло. Вы должны остановить поток с логическим условием или чем-то подобным.

Проверьте это:

http://www.interact -sw.co.uk / iangblog / 2004/11/12 / отмена

...