2009-04-29 4 views
47

Я пишу приложение Windows, которое многократно выполняет последовательность цифровых операций ввода-вывода.Как полностью «убить» рабочего фонаря?

Эта последовательность действий начинается, когда пользователь нажимает кнопку «СТАРТ», и это делается фоновым работником в backgroundWorker1_DoWork().

Однако, есть случаи, когда я получаю сообщение об ошибке «Этот рабочий стол в настоящее время .......».

Я имею в виду реализации следующих в коде, используя время цикла, чтобы «убить» фона рабочего до начала другой последовательности действий:

if (backgroundWorker1.IsBusy == true) 
{ 

    backgroundWorker1.CancelAsync(); 
    while (backgroundWorker1.IsBusy == true) 
    { 
     backgroundWorker1.CancelAsync(); 
    } 

    backgroundWorker1.Dispose(); 

} 

backgroundWorker1.RunWorkerAsync(); 

Я думаю, моя основная проблема в том, будет ли backgroundWorker1 будет «убит» в конце концов? Если так будет, потребуется много времени для его завершения?

Будет ли это кодирование заводить меня в бесконечный цикл?

+2

Зачем вам нужен цикл? Разве не убил бы это когда-нибудь, значит, он больше не занят? – Louis

+0

Кроме того, было бы разумно поспать там, что подходит для приложения. Также, что происходит, если рабочий законно занят? Вы все еще хотите, чтобы это убило его в середине процесса? Я думаю, что было бы разумнее остановить то, что когда-либо заставляло фонового работника становиться бесконечно занятым. – Louis

ответ

16

Я считаю, что потоки должны нести ответственность за свои собственные ресурсов, насколько это практически возможно, , включая их собственную жизнь.

Как правило, это плохая идея, чтобы убивать потоки из-за пределов их возможностей. Приложения, которые спроектированы для передачи сообщения в поток для закрытия себя, имеют тенденцию иметь гораздо меньше проблем, связанных с многопоточным поведением.

Поток должен отслеживать указанное сообщение, которое может быть таким же простым, как булево значение, заданное другим потоком и считанное этим потоком мониторинга, своевременно и автоматически закрывать , как только это возможно.

Это означает, что, если он должен искать сообщения:

  • в его основной цикл, если таковые имеются.
  • периодически в любых длительных циклах.

Поток, закрывающий сообщение, должен ждать (но не останавливать GUI, конечно).

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

Но все же, как правило, проще просто архитектовать приложение, чтобы оставить мастера потоков своей собственной судьбой.

+2

Если поток обращается к внешнему ресурсу, который может блокировать поток, то убийство потока может быть единственным вариантом – HamoriZ

+0

Следовательно, фраза: «насколько это практически возможно» :-) – paxdiablo

-1

У меня была такая же проблема, я не уверен, поможет ли это, но я предполагаю, что у вашего рабочего рабочего есть петля внутри или она выйдет. Что вам нужно сделать, это поставить свою петлю внутри.

Put внутри фона рабочего:

while (backgroundworker1.CancellationPending == false) 
{ 
    //Put your code in here 
} 

Чтобы убить этого BackgroundWorker, вы можете положить в кнопку:

BackgroundWorker1.CancelAsync() 

Я надеюсь, что это помогает.

+3

Пожалуйста, очистите свое форматирование и на английском! –

+1

Действительный ответ, но ужасное форматирование ... – John

+0

Пожалуйста, научитесь говорить. – ro0ter

69

Вы можете использовать что-то вроде этого (для получения дополнительной информации о прерывании управляемых потоков и о ThreadAbortException см "Plumbing the Depths of the ThreadAbortException Using Rotor" Крис Sells):

public class AbortableBackgroundWorker : BackgroundWorker 
{ 

    private Thread workerThread; 

    protected override void OnDoWork(DoWorkEventArgs e) 
    { 
     workerThread = Thread.CurrentThread; 
     try 
     { 
      base.OnDoWork(e); 
     } 
     catch (ThreadAbortException) 
     { 
      e.Cancel = true; //We must set Cancel property to true! 
      Thread.ResetAbort(); //Prevents ThreadAbortException propagation 
     } 
    } 


    public void Abort() 
    { 
     if (workerThread != null) 
     { 
      workerThread.Abort(); 
      workerThread = null; 
     } 
    } 
} 

Использование:

backgroundWorker1 = new AbortableBackgroundWorker(); 
//... 
backgroundWorker1.RunWorkerAsync(); 

if (backgroundWorker1.IsBusy == true) 
{ 
    backgroundWorker1.Abort(); 
    backgroundWorker1.Dispose(); 
} 
+0

Мне это очень нравится. Кажется, это делает работу. У меня есть некоторые сложные вещи, которые происходят в BackgroundWorker, но ничего не получается до тех пор, пока оно не завершится. –

+3

Кстати, по-прежнему опасно убивать поток с помощью Thread.Abort, потому что не каждый класс в .NET Framework устойчив в терминах асинхронных исключений. Но если вы знаете, что вы делаете - все в порядке. –

+0

Я был очень рад найти этот код вначале, но когда я попробовал это, потребовалось много времени для потока, чтобы прервать ... Я не ожидал такого поведения, и я немного смущен. На самом деле нет идеи, почему это так долго –

0

Я положил одну вместе что (я думаю) делает работу. Пожалуйста, дайте мне знать, если im waaaay выключен. Вот простой пример того, как он работает.

var backgroundWorker = new BackgroundWorker(){WorkerSupportsCancellation = true}; 

backgroundWorker.DoWork += (sender, args) => 
     {     
       var thisWorker = sender as BackgroundWorker; 
       var _child = new Thread(() => 
               { 
                //..Do Some Code 

               }); 
       _child .Start(); 
       while (_child.IsAlive) 
       { 
        if (thisWorker.CancellationPending) 
        { 
         _child.Abort(); 
         args.Cancel = true; 
        } 
        Thread.SpinWait(1); 
       }     
     }; 

backgroundWorker.RunWorkerAsync(parameter); 
//..Do Something... 
backgroundWorker.CancelAsync(); 

Поскольку рабочий фон является частью пула потоков, мы не хотим его прерывать. Но мы можем запустить поток внутри, который мы можем разрешить. Затем BackgroundWorker запускается до тех пор, пока ни один дочерний поток не будет завершен, или мы не сообщаем ему, чтобы убить процесс. Затем фоновый рабочий поток может вернуться в пул чтения. Обычно я завершаю это в классе-помощнике и передаю метод делегата, который я хочу, чтобы фоновый поток запускался в качестве параметра и запускал его в дочернем потоке.

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

+0

-1: для '.Abort()' в новом дизайнерском предложении. –

+2

Очень, очень опасно для 'Abort' поток извне. Пожалуйста, прочитайте: http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation. В принципе, это очень неверно в ** трех ** способах: (1) создание ненужной дополнительной нити, (2) ожидание на ней и (3) прерывание ее в потенциально критический момент без правильной очистки. – Aaronaught

+0

В ответе выше также используется решение типа child-thread + abort. Есть ли лучший способ справиться с этим? – Rob

-1
public class abortableBackgroundworker: BackgroundWorker 
{ 
    public bool Kill = false; 

    protected override void OnDoWork(DoWorkEventArgs e) 
    { 


     var _child = new Thread(() => 
     { 
      while (!Kill) 
      { 

      } 
      e.Cancel = true;//..Do Some Code 

     }); 
     _child.Start(); 
     base.OnDoWork(e); 

    } 



} 

установить убить, чтобы верно убить нить и не проблема прерывания :)

+5

Чувак этот код убил мой 24-Core Xeon xD –

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