Подтвердить что ты не робот

С#: Мне нужно избавиться от BackgroundWorker, созданного во время выполнения?

Обычно у меня такой код на форме:

    private void PerformLongRunningOperation()
    {
        BackgroundWorker worker = new BackgroundWorker();

        worker.DoWork += delegate
        {
            // perform long running operation here
        };

        worker.RunWorkerAsync();
    }

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

Это вызовет проблемы? Правильнее ли объявлять уровень модуля _saveWorker, а затем вызывать Dispose на нем из метода dispose формы?

4b9b3361

Ответ 1

Да, вы должны избавиться от рабочего фона.

Вам может быть проще использовать ThreadPool.QueueUserWorkItem(...), который впоследствии не требует очистки.


Дополнительная информация о том, почему вы всегда должны вызывать Dispose():

Хотя, если вы посмотрите в классе BackgroundWorker, на самом деле он не выполняет очистку потока в нем методом dispose, по-прежнему важно вызвать Dispose из-за эффекта, который класс имеет в сборщике мусора.

Классы с финализаторами немедленно не сгруппированы. Они сохраняются и добавляются в очередь финализатора. Затем запускается поток финализатора (который следует за стандартными вызовами шаблонов). Это означает, что объект будет выживать в генерацию GC 1. И коллекции gen 1 намного реже, чем коллекции gen 0, поэтому вы пытаетесь задерживаться в памяти намного дольше.

Если вы вызываете Dispose(), объект не будет добавлен в очередь финализации, поэтому он может быть собран в мусор.

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

Итак, я полагаю, в целом, это не 100% жесткое и быстрое требование. Ваше приложение не будет взорваться (или даже утечка памяти), если вы не вызываете Dispose(), но в некоторых случаях могут иметь негативные последствия. Фоновый работник был разработан для использования как компонент WinForms, поэтому используйте его таким образом, если у вас разные требования и вы не хотите использовать его в качестве компонента WinForms, не используйте его, используйте правильный инструмент для задания, как ThreadPool.

Ответ 2

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

BackgroundWorker действительно предназначен для использования в качестве компонента в форме WinForms, поэтому я бы рекомендовал либо сделать это, либо переключиться на что-то вроде Thread.QueueUserWorkItem. Это будет использовать поток потока пула и не будет требовать специальной очистки после его завершения.

Ответ 3

На мой взгляд, в общем случае, если он IDisposable, он должен быть Dispose() d, когда вы закончите с ним. Даже если текущая реализация BackgroundWorker технически не нужна, вы не хотите удивляться более поздним внутренним реализациям, которые могут быть.

Ответ 4

Я бы не стал беспокоиться, единственным ресурсом, который Bgw может удержать, является Thread и если у вас нет бесконечного цикла в вашем делегате, тогда вы в порядке.

BackgroundWorker наследует IDisposable() от Component, но на самом деле не нуждается в нем.

Сравните это с нажатием вашего метода непосредственно на ThreadPool. Вы не можете (не можете) Dispose threads, конечно, не те из пула.

Но если ваш образец завершен в том смысле, что вы не используете объекты Completed или Progress/Cancel, вы могли бы просто использовать ThreadPool.QueueUserWorkItem().

Ответ 5

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

private void PerformLongRunningOperation()
    {
        using (BackgroundWorker worker = new BackgroundWorker())
        {
            worker.DoWork += delegate
                             {
                                 // perform long running operation here 
                             };
            worker.RunWorkerAsync();
        }
    }

EDIT:

Хорошо, я собрал небольшое испытание, чтобы посмотреть, что происходит с утилизацией и еще много чего:

using System;
using System.ComponentModel;
using System.Threading;

namespace BackgroundWorkerTest
{
    internal class Program
    {
        private static BackgroundWorker _privateWorker;

        private static void Main()
        {
            PrintThread("Main");
            _privateWorker = new BackgroundWorker();
            _privateWorker.DoWork += WorkerDoWork;
            _privateWorker.RunWorkerCompleted += WorkerRunWorkerCompleted;
            _privateWorker.Disposed += WorkerDisposed;
            _privateWorker.RunWorkerAsync();
            _privateWorker.Dispose();
            _privateWorker = null;

            using (var BW = new BackgroundWorker())
            {
                BW.DoWork += delegate
                                 {
                                     Thread.Sleep(2000);
                                     PrintThread("Using Worker Working");
                                 };
                BW.Disposed += delegate { PrintThread("Using Worker Disposed"); };
                BW.RunWorkerCompleted += delegate { PrintThread("Using Worker Completed"); };
                BW.RunWorkerAsync();
            }

            Console.ReadLine();
        }

        private static void WorkerDisposed(object sender, EventArgs e)
        {
            PrintThread("Private Worker Disposed");
        }

        private static void WorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            PrintThread("Private Worker Completed");
        }

        private static void WorkerDoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(2000);
            PrintThread("Private Worker Working");
        }

        private static void PrintThread(string caller)
        {
            Console.WriteLine("{0} Thread: {1}", caller, Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Вот результат:

Main Thread: 1
Private Worker Disposed Thread: 1
Using Worker Disposed Thread: 1
Private Worker Working Thread: 3
Using Worker Working Thread: 4
Using Worker Completed Thread: 4
Private Worker Completed Thread: 3

Из некоторых тестов кажется, что Dispose() практически не влияет на инициированный BackgroundWorker. Независимо от того, вызываете ли вы его в рамках используемого оператора или используете его в коде, и немедленно его утилизируйте и разыщите, он все еще работает нормально. Выбранное событие происходит в основном потоке, а DoWork и RunWorkerCompleted встречаются в потоках пула потоков (независимо от того, какой из них доступен при срабатывании события). Я попробовал случай, когда я незарегистрировал событие RunWorkerCompleted сразу после того, как я вызвал Dispose (так что до DoWork был шанс завершить), а RunWorkerCompleted не срабатывал. Это заставляет меня поверить, что вы все еще можете манипулировать объектом BackgroundWorker, несмотря на то, что он удален.

Так, как уже отмечали другие, на данный момент это похоже на то, что вызов Dispose на самом деле не требуется. Однако я не вижу никакого вреда в этом, по крайней мере, из своего опыта и этих тестов.

Ответ 6

Вызовите в своем RunWorkerCompleted событии.

BackgroundWorker wkr = new BackgroundWorker();
wkr.DoWork += (s, e) => {
    // Do long running task.
};
wkr.RunWorkerCompleted += (s, e) => {
    try {
        if (e.Error != null) {
            // Handle failure.
        }
    } finally {
        // Use wkr outer instead of casting.
        wkr.Dispose();
    }
};
wkr.RunWorkerAsync();

Дополнительный try/finally должен гарантировать, что вызов Dispose вызывается, если ваш код завершения вызывает исключение.

Ответ 7

Для всех объектов IDisposable рекомендуется использовать Dispose(). Это позволяет им освобождать неуправляемые ресурсы, которые они могут удерживать, например, "Ручки". В классах IDisposable также должны быть финализаторы, присутствие которых может задерживать время, когда GC позволяет полностью собирать эти объекты.

Если ваш класс выделяет IDisposable и присваивает его переменной-члену, тогда в целом он также должен быть IDisposable.

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

Ответ 8

Обработчик завершения запускается в исходном потоке (т.е. не в фоновом потоке из пула потоков)! Результаты теста фактически подтверждают это предположение.