BackgroundWorker проти фонової теми


166

У мене є стилістичне запитання щодо вибору реалізації фонових ниток, які я повинен використовувати у додатку форми Windows. В даний час у мене є BackgroundWorkerформа, яка має нескінченний (while(true))цикл. У цьому циклі я використовую, WaitHandle.WaitAnyщоб тримати відкладення потоку, доки не трапиться щось цікаве. Однією з ручок події, на яку я чекаю, є StopThreadподія, яка " " може вирватися з циклу. Ця подія сигналізується, коли з моєї переоцінки Form.Dispose().

Я десь прочитав, що BackgroundWorkerдійсно призначено для операцій, з якими ви не хочете зв’язувати інтерфейс користувача та мати кінцевий кінець - як завантаження файлу чи обробка послідовності елементів. У цьому випадку "кінець" невідомий і лише тоді, коли вікно закрите. Тому чи було б більш доцільним для мене використовувати фонову нитку замість BackgroundWorkerцього?

Відповіді:


88

З мого розуміння вашого питання, ви використовуєте BackgroundWorkerстандартну нитку.

Причина, чому BackgroundWorkerрекомендується використовувати речі, з якими ви не хочете зв’язувати потік інтерфейсу, полягає в тому, що це розкриває деякі приємні події під час розробки Win Forms.

Події люблять RunWorkerCompletedсигналізувати, коли нитка виконала те, що їй потрібно було зробити, і ProgressChangedподія для оновлення графічного інтерфейсу в потоці прогресує.

Тому якщо ви не використовуєте їх, я не бачу шкоди у використанні стандартної теми для того, що вам потрібно зробити.


ще одне питання, в якому я не впевнений, це, припустимо, я намагаюся розпоряджатися формою, на якій працює фоновий працівник. Я надсилаю сигнал shutdownnevent (ManualResetEvent) і десь після цього DoWork витончено вийде. Чи варто мені просто дозволити форму продовжувати та розпоряджатися, навіть якщо DoWork може зайняти трохи більше часу, щоб закінчити, чи є якийсь шлях (і чи краще це), щоб підключити тему. форми продовжувати?
Фредді Сміт

Я думаю, що BackgroundWorker.IsBusy - це те, що ти шукаєш там.
ParmesanCodice

1
Використовуйте лише CancelAsync(і протестуйте, CancellationPendingчи буде ваш потік опитуватися через короткі проміжки часу, якщо ви хочете, щоб виняток було піднято натомість, використовуйте, System.Threading.Thread.Abort()що збільшує виняток у самому блоці потоку, виберіть правильну модель для ситуації.
Brett Ryan

369

Деякі мої думки ...

  1. Використовуйте BackgroundWorker, якщо у вас є одне завдання, яке працює у фоновому режимі і потребує взаємодії з інтерфейсом користувача. Завдання збору даних та викликів методів до потоку користувальницького інтерфейсу обробляється автоматично через його модель на основі подій. Уникайте BackgroundWorker, якщо ...
    • ваша збірка не має або не взаємодіє безпосередньо з інтерфейсом користувача,
    • вам потрібно, щоб нитка була передньою ниткою, або
    • потрібно маніпулювати пріоритетом потоку.
  2. Використовуйте нитку ThreadPool, коли бажана ефективність. ThreadPool допомагає уникнути накладних витрат, пов’язаних із створенням, запуском та зупинкою ниток. Уникайте використання ThreadPool, якщо ...
    • завдання виконується протягом вашої програми,
    • вам потрібна нитка, щоб бути ниткою переднього плану,
    • вам потрібно маніпулювати пріоритетом потоку, або
    • вам потрібна нитка, щоб мати фіксовану ідентичність (переривання, призупинення, виявлення).
  3. Використовуйте клас " Нитка" для тривалих завдань і тоді, коли вам потрібні функції, запропоновані офіційною моделлю нитки, наприклад, вибір між переднім планом та фоновими нитками, налаштування пріоритету нитки, тонкозернистий контроль над виконанням потоку тощо.

10
Фоновий працівник знаходиться у збірці System.dll та просторі імен System.ComponentModel. Не існує залежності від Winforms.
Кугель

17
Це правильно, але BackgroundWorkerрозроблено так, щоб повідомити про хід потоку зацікавленій стороні, що зазвичай включає інтерфейс користувача. Документація MSDN для класу робить це досить зрозумілим. Якщо вам просто потрібне завдання, яке потрібно виконати на задньому плані, віддайте перевагу використанню ThreadPoolнитки.
Метт Девіс

5
Що стосується Вашого питання щодо System.Windows.Formsскладання; BackgroundWorkerтакож корисний і для програм WPF, і вони можуть не мати посилання на WinForms.
GiddyUpHorsey

12

Досить багато, що сказав Метт Девіс, з наступними додатковими пунктами:

Для мене головним диференціатором BackgroundWorkerє автоматичний перегляд завершеної події через SynchronizationContext. У контексті інтерфейсу це означає, що завершена подія запускається в потоці користувальницького інтерфейсу, і тому може бути використана для оновлення інтерфейсу користувача. Це головний диференціатор, якщо ви використовуєте BackgroundWorkerв контексті інтерфейсу користувача.

Завдання, виконані через програму ThreadPoolнеможливо легко скасувати (це включає в себе ThreadPool. QueueUserWorkItemІ делегати виконують асинхронно). Тож, хоча це дозволяє уникнути накладних обтікань потоку, якщо вам потрібно скасувати або скористайтеся BackgroundWorkerабо (швидше за все, поза інтерфейсом), накручуйте нитку і зберігайте посилання на неї, щоб ви могли зателефонувати Abort().


1
Лише ... сподіваємось, що програма розроблена про чистий метод зупинки потокового завдання (Abort - це взагалі не так)

11

Крім того, ви зав'язуєте різьблення з нитками протягом усього життя фонового працівника, що може викликати занепокоєння, оскільки їх існує лише обмежена кількість. Я б сказав, що якщо ви коли-небудь створюєте потік один раз для свого додатка (і не використовуєте жодної з функцій фонового працівника), тоді використовуйте нитку, а не низку backgroundworker / threadpool.


1
Я думаю, що це хороший момент. Тож повідомлення, яке я беру з цього, - це використовувати фоновий працівник, якщо вам потрібна фонова нитка, "тимчасово" протягом життя Форми, однак якщо вам потрібна фонова нитка протягом усього періоду роботи форми (що може бути хвилин, годин, днів ...) потім використовуйте Thread замість BackgroundWorker, щоб не зловживати метою ThreadPool
Фредді Сміт

Щодо: "... що може викликати занепокоєння, оскільки їх існує лише обмежене число", ви маєте на увазі, що інші програми в ОС можуть їм знадобитися і ділитися з того ж "пулу"?
Dan W

8

Знаєте, іноді просто просто працювати з BackgroundWorker незалежно від того, використовуєте ви Windows Forms, WPF або будь-яку іншу технологію. Акуратна частина цих хлопців полягає в тому, що ви набираєте різьбу, не турбуючись занадто сильно про те, де ви виконуєте нитку, що чудово підходить для простих завдань.

Перш ніж використовувати BackgroundWorkerспочатку, якщо ви хочете скасувати потік (закриття програми, скасування користувача), тоді вам потрібно вирішити, чи має ваш потік перевірити на скасування або чи слід підштовхувати його до виконання.

BackgroundWorker.CancelAsync()буде встановлений CancellationPendingв trueале нічого не буде робити більше, це те відповідальність нитки постійно перевіряти це, мати на увазі також , що ви могли б у кінцевому підсумку з гонки в цьому підході , де скасованого ваш користувач, але нитка завершена до тестування на CancellationPending.

Thread.Abort() з іншого боку, викине виняток у потоці виконання потоку, який примушує скасувати цей потік, ви повинні бути обережними щодо того, що може бути небезпечним, якщо цей виняток раптово виник у ході виконання.

Нитка потребує дуже ретельного розгляду незалежно від того, яке завдання, для подальшого читання:

Паралельне програмування в .NET Framework Управління потоками найкращих практик


5

Я знав, як використовувати нитки, перш ніж я знав .NET, тому знадобилося деяке звикання, коли я почав використовувати BackgroundWorkers. Метт Девіс узагальнив різницю з великою досконалістю, але додав би, що важче зрозуміти, що саме робить код, і це може зробити налагодження важче. Простіше думати про створення та вимкнення потоків, IMO, ніж думати про надання роботи пулу потоків.

Я до сих пір не можу коментувати повідомлення інших людей, тому пробачте мою кульгавість у використанні відповіді на адресу piers7

Не використовуйте, Thread.Abort();замість цього, сигналізуйте про подію та конструюйте свій потік, щоб він закінчувався вишукано, коли ви отримали сигнал. Thread.Abort()піднімає ThreadAbortExceptionдовільну точку виконання потоку, яка може робити всілякі нещасні речі, такі як "Монітори сиріт", корумпований загальний стан тощо.
http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx


2

Якщо він не зламався - виправте це, поки це ... просто жартую :)

Але серйозно BackgroundWorker, мабуть, дуже схожий на те, що у вас вже є, якби ви починали з нього з самого початку, можливо, ви б заощадили деякий час - але в цьому моменті я не бачу потреби. Якщо щось не працює, або ви вважаєте, що ваш поточний код важко зрозуміти, я б дотримувався того, що у вас є.


2

Основна відмінність - це, як ви заявили, - генерування подій GUI з BackgroundWorker. Якщо потоці не потрібно оновлювати дисплей або генерувати події для основного потоку GUI, то це може бути простою ниткою.


2

Я хочу вказати на одну поведінку класу BackgroundWorker, про який ще не було сказано. Ви можете зробити звичайний Thread для запуску у фоновому режимі, встановивши властивість Thread.IsBackground.

Фонові нитки ідентичні потокам переднього плану, за винятком того, що фонові нитки не перешкоджають завершенню процесу. [ 1 ]

Ви можете перевірити цю поведінку, зателефонувавши в конструкторі вікна форми наступного методу.

void TestBackgroundThread()
{
    var thread = new Thread((ThreadStart)delegate()
    {
        long count = 0;
        while (true)
        {
            count++;
            Debug.WriteLine("Thread loop count: " + count);
        }
    });

    // Choose one option:
    thread.IsBackground = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

Якщо для властивості IsBackground встановлено значення true, і ви закриєте вікно, тоді ваша програма завершиться нормально.

Але коли для IsBackground властивість встановлено значення false (за замовчуванням), і ви закриєте вікно, тоді просто вікно не буде діяти, але процес все ще триватиме.

Клас BackgroundWorker використовує нитку, яка працює у фоновому режимі.


1

Фоновий працівник - це клас, який працює в окремому потоці, але він забезпечує додаткову функціональність, яку ви не отримуєте за допомогою простої теми (наприклад, обробка звіту про виконання завдань).

Якщо вам не потрібні додаткові функції, надані фоновим працівником, - а здається, що ви цього не зробите - тоді тема буде більш доречною.


-1

Мене викликає здивування те, що дизайнер візуальної студії дозволяє використовувати лише BackgroundWorkers та Таймери, які насправді не працюють із сервісним проектом.

Це дає вам акуратне управління перетягуванням на вашу послугу, але ... навіть не намагайтеся розгорнути його. Не вийде.

Послуги: Використовуйте лише System.Timers.Timer System.Windows.Forms.Timer не працюватиме, навіть якщо він доступний у панелі інструментів

Послуги: BackgroundWorkers не працюватимуть, коли він працює як служба Використовуйте System.Threading.ThreadPools замість цього або Async виклики

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.