Как полностью убить фонового рабочего?

Я пишу приложение для Windows, которое многократно выполняет последовательность действий цифрового ввода-вывода.

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

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

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

if (backgroundWorker1.IsBusy == true)
{

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

    backgroundWorker1.Dispose();

}

backgroundWorker1.RunWorkerAsync();

Я думаю, что меня больше всего беспокоит, будет ли в конечном итоге "убит" backgroundWorker1? Если будет, долго ли это займет?

Заставит ли это кодирование бесконечный цикл?


person Community    schedule 29.04.2009    source источник
comment
Зачем тебе петля? Разве его убийство не означало бы, что он больше не занят?   -  person Louis    schedule 29.04.2009
comment
Кроме того, было бы разумно положить туда сон, который подходит для приложения. Кроме того, что произойдет, если работник законно занят? Вы все еще хотите, чтобы он убивал его посреди процесса? Я думаю, что было бы разумнее прекратить то, что когда-либо заставляло фонового работника становиться бесконечно занятым.   -  person Louis    schedule 29.04.2009


Ответы (6)


Вы можете использовать что-то вроде этого (для получения дополнительной информации о прерывании управляемых потоков и об исключении ThreadAbortException см. "Анализ глубины исключения ThreadAbortException с помощью ротора "Криса Селлса):

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();
}
person Sergey Teplyakov    schedule 20.01.2010
comment
Мне это очень нравится. Кажется, он действительно выполняет свою работу. У меня происходят некоторые сложные вещи в BackgroundWorker, но ничего не пишется, пока он не завершится. - person Tom Anderson; 22.08.2011
comment
Кстати, убивать поток с помощью Thread.Abort по-прежнему опасно, потому что не каждый класс в .NET Framework устойчив к асинхронным исключениям. Но если знаешь, что делаешь - ничего страшного. - person Sergey Teplyakov; 22.08.2011
comment
Сначала я был очень счастлив найти этот код, но когда я попробовал это, потребовалось много времени для прерывания потока ... Я не ожидал такого поведения и немного сбит с толку. На самом деле понятия не имею, почему это занимает так много времени - person Mehdi LAMRANI; 28.06.2012
comment
Есть несколько советов: Thread.Abort не может прервать поток, когда поток выполняется: 1) окончательные блоки, 2) критические области выполнения, 3) неуправляемый код. Это означает, что если ваш код выполняет сторонний код или какой-либо другой код, вы не можете предсказать, когда этот поток будет прерван. Взгляните на сообщение Эрика: blogs.msdn.com/b/ericlippert/archive/2010/02/22/ - person Sergey Teplyakov; 30.06.2012
comment
@SergeyTeplyakov, если я запускаю этот AbortableBackgroundWorker из пользовательского интерфейса, разве текущий поток не является пользовательским интерфейсом? {workerThread = Thread.CurrentThread;} ​​В прерывании текущего потока нет ничего плохого? - person Emi; 27.12.2012
comment
Это круто! прекрасная работа :) - person Ehsan Sajjad; 13.04.2016
comment
Внимание: этот код содержит состояние гонки. Вы не можете прервать поток, пока он не выполнил строку workerThread = Thread.CurrentThread; - person Pavel Mayorov; 28.03.2017
comment
Здравствуйте, после долгих трудностей с CancelAsync (), наконец, я использую ваш код. Не могли бы вы помочь мне выйти из тупика? stackoverflow.com/questions/47952915/ - person Kay Lee; 23.12.2017
comment
Я превращаю пример Microsoft для приложения командной строки TCPListener в фонового работника. DoWork () сидит на блокирующем вызове TcpClient client = _server.AcceptTcpClient (); Вызов Abort не выбивает abortablebackgroundworker из его DoWork () в рамках .NET 3.5. Я думаю, что я попаду в трясину состояния гонки, сделав это правильно асинхронно с BeginAcceptTCPClient. Условия гонки значительно увеличивают нагрузку на тестирование. Черт возьми. - person user922020; 05.06.2018

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

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

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

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

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

Поток, завершающий его с сообщением, должен подождать (но, конечно, не останавливайте графический интерфейс).

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

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

person paxdiablo    schedule 29.04.2009
comment
Если поток обращается к внешнему ресурсу, который может блокировать поток, то единственным вариантом может быть уничтожение потока. - person HamoriZ; 07.08.2012
comment
Отсюда фраза: насколько это возможно :-) - person paxdiablo; 07.08.2012

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

Поместите внутрь своего фонового рабочего:

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

Чтобы убить этого фонового работника, вы можете вставить свою кнопку:

BackgroundWorker1.CancelAsync()

Надеюсь, это поможет.

person echrom    schedule 12.01.2010

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

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 в основном выполняется до тех пор, пока дочерний поток не будет завершен, или пока мы не дадим ему сигнал убить процесс. Затем фоновый рабочий поток может вернуться в пул чтения. Обычно я заключаю это во вспомогательный класс и передаю метод делегата, который я хочу, чтобы фоновый поток запускался, передаваемый в качестве параметра, и запускал его в дочернем потоке.

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

person Rob    schedule 07.04.2010
comment
Очень, очень опасно для Abort потока извне. Прочтите: interact-sw.co.uk/iangblog/2004 / 11/12 / отмена. По сути, это очень-очень неправильно по трем причинам: (1) создание ненужного дополнительного потока, (2) ожидание занятости и (3) его прерывание в потенциально критический момент, не допуская надлежащего уборка. - person Aaronaught; 07.04.2010
comment
В приведенном выше ответе также используется решение типа дочерний поток + прерывание. Есть ли лучший способ справиться с этим? - person Rob; 07.04.2010

я просто использую «возврат», чтобы перейти к концу потока:

private void myWorker_DoWork(object sender, DoWorkEventArgs e)
{
   // some variables 

   while(true)
   {
      // do something ...
      if(need_cancel) return;
      else doSomeThingElse();
   }

   // may be more code here (ignored if 'return' is executed) ...
   // .........
   // .........
}
person Danny85    schedule 09.10.2020

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

    }



}

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

person Daniel    schedule 04.09.2011