2010-11-15 2 views
5

В этом коде, когда 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(); 
} 
+0

Есть ли причина, по которой вы не можете использовать BackgroundWorker или аналогичную конструкцию? Нерестные потоки включают много накладных расходов, поэтому имеет смысл повторно использовать их, когда это возможно. –

ответ

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 page:

Когда поток вызывает Прервать на себя, эффекта аналогичен выбрасывание исключения; ThreadAbortException происходит немедленно, и результат предсказуем. Однако, если один поток вызывает прерывание в другом потоке, прерывает прерывает любой код работает. Существует также вероятность того, что статический конструктор может быть прерван. В редких случаях это может предотвратить экземпляры этого класса от , созданные в этом домене приложения. В версии версии .NET Framework 1.0 и 1.1 существует вероятность того, что поток может прерваться, тогда как окончательный блок работает , и в этом случае блок finally прерван.

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

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

+0

Хорошее описание. Но я думаю, что «foreach (var thread in threads)» может вызвать исключение, здесь должен использоваться «lock». – acoolaum

+0

@acoolaum: Спасибо. Насколько я могу судить, «замок» здесь не нужен. Оба обработчика работают в цикле сообщений формы; они не могут работать одновременно на разных потоках. – Ani

+0

Да, вы правы. – acoolaum

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; 
      }     
     } 
    } 
} 
+0

Одна из проблем заключается в блокировании пользовательского интерфейса, пока он не сможет присоединиться к t1 –

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

    } 
} 

Однако, я хотел бы рассмотреть чтении на Threading в. NET using one these guides (Я бы рекомендовал Joseph Albahari's guide on aborting - автору C# в двух словах), а не использовать этот метод, особенно если вы выполняете операции ввода-вывода или базы данных, которые могут покидать объекты в неожиданных состояниях.

Смежные вопросы