C#: я должен расположить BackgroundWorker, созданный во времени выполнения?

6 ответов

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

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


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

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

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

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

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

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

32
ответ дан 27 November 2019 в 20:22
поделиться

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

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

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

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

6
ответ дан 27 November 2019 в 20:22
поделиться

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

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

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

2
ответ дан 27 November 2019 в 20:22
поделиться

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

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

16
ответ дан 27 November 2019 в 20:22
поделиться

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

7
ответ дан 27 November 2019 в 20:22
поделиться

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

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

РЕДАКТИРОВАТЬ:

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

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

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

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

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

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

4
ответ дан 27 November 2019 в 20:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: