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

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

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

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

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

if (backgroundWorker1.IsBusy == true)
{

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

    backgroundWorker1.Dispose();

}

backgroundWorker1.RunWorkerAsync();

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

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

5 ответов

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

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

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

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

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

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

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

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

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

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

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

Поместите в свой фоновый работник:

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

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

BackgroundWorker1.CancelAsync()

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

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

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

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

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

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) ...
   // .........
   // .........
}
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, чтобы уничтожить поток и не прерывать проблему:)

Другие вопросы по тегам