2015-11-02 2 views
0

Я создаю новый BackgroundWorker каждый раз, когда мне нужно выполнить какую-либо задачу Async и после завершения работы, но я сомневаюсь, что это полностью ее использует, потому что метод Dispose реализован по Системе. ComponentModel.ComponentСоздание новой BackgroundWorker для каждой задачи async

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

Большая часть работы async - это ввод-вывод, и я не могу использовать async/await, потому что я использую .NET 4 (не могу использовать 4.5).

Я завернул все это в этом методе:

public void AsyncDo(Action Action, Action ActionFinish = null) 
{ 
    using (BackgroundWorker bw = new BackgroundWorker()) { 
     bw.DoWork +=() => Action(); 
     bw.RunWorkerCompleted += (s, e) => 
     { 
      if (ActionFinish != null) 
       ActionFinish(); 
      if (e.Error != null) 
       OnException(e.Error); 
     }; 
     bw.RunWorkerAsync(); 
    } 
} 

Исходя из ответов, я обновил его с

Task t = new Task(action); 
t.Start(); 

Но я получаю ошибку кросс-нить, когда я сделайте actionFinish(), объединив его с action или позвонив по телефону t.ContinueWith(). Это было не так в BGW, это не требовало вызова для RunWorkerCompleted. Я не могу изменить каждый вызов этого метода, чтобы заставить их использовать вызов, что мне делать?

+0

BackgroundWorker предназначен только для сценариев пользовательского интерфейса. Звучит подозрительно, что у вас их будет 1000. Что это за GUI? – usr

+6

Не надо. Задачи полностью заменяют BackgroundWorker. BackgroundWorker существует только для обратной совместимости и намного тяжелее Заданий. Это также совершенно непригодно для асинхронной работы - нет возможности комбинировать асинхронные операции, например, с помощью 'async/await' –

+0

Что вы делаете? Нет причин использовать тысячи или миллионы задач, а тем более BackgroundWorkers. Для обработки большого количества данных используйте Parallel.For или PLINQ. Чтобы запросить запросы, используйте ActionBlock. Чтобы выполнить работу асинхронно за пользовательским интерфейсом, используйте 'async/await'. –

ответ

4

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

Если вам нужно выполнить какую-либо асинхронную задачу, просто используйте Task - вот для чего она предназначена. Если у вас нет .NET 4+, вам нужно будет использовать Thread (или лучше, ThreadPool.QueueUserWorkItem), но это еще сложнее (хотя обманчиво просто). Как всегда, http://www.albahari.com/threading/ должны быть отправной точкой при попытке реализовать многопоточность (или асинхронный код вообще) :)

EDIT:

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

var task = Task.Factory.StartNew(yourAction); 
task.ContinueWith(yourResultAction, TaskScheduler.FromCurrentSynchronizationContext()); 

Все это сильно типизированных, так что это довольно легко пройти произвольное значение из yourAction задачи (бег на рабочем потоке) в yourResultAction (работает в потоке пользовательского интерфейса). Обратите внимание, что TaskScheduler.FromCurrentSynchronizationContext() должен быть запущен в потоке пользовательского интерфейса, конечно.

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

Кроме того, Microsoft.Bcl.Async приносит большую часть .NET 4.5 + s await благость к .NET 4.0 - в конце концов, await все в компилятор, он не нуждается в новой среде выполнения работы. Работа с await намного проще, чем работа с продолжением, особенно при обработке ошибок.

+1

На самом деле, BGW просто совместим в 4.5. Один Task.Run с процессом обеспечивает ту же функциональность. –

+0

@ PanagiotisKanavos BGW по-прежнему является компонентом. Есть много людей, которые предпочитают создавать свои приложения с использованием компонентов, а не функций. Delphi воспользовался этим эффектом еще в один прекрасный день :) Это не значит, что я бы использовал «BackgroundWorker» - но это тоже не устарело. Есть еще несколько способов кошки кошки. – Luaan

+0

Я обычно добавляю BackGroundWorker в класс и делаю класс IDisposible. Затем я могу удалить класс, когда задача будет завершена. – jdweng

-4

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

Imports System.ComponentModel 
Module Module1 

    Sub Main() 
     Dim backgroundWorker As New MyBackGroundWorker 
     backgroundWorker.Dispose() 
    End Sub 

End Module 
Class MyBackGroundWorker : Implements IDisposable 

    Dim backgroundWorker As New BackgroundWorker 

    Sub Dispose() Implements IDisposable.Dispose 

     Me.Dispose() 
    End Sub 
End Class 
+0

Не понимаю. Ваш класс не реализует ничего нового, это то же самое, что и обычный BGW, нормальный BGW также реализует IDisposable, поэтому вы можете вызвать '.Dispose()' – Elmo

+0

С помощью моего метода вы также можете распоряжаться другим ассоциированным объектом с backgroundWorker в методе dispose класса , Мой метод будет располагать все объекты в классе. – jdweng

+0

(Я не был нисходящим) Да, но ваш ответ не добавляет ничего нового. – Elmo

3

Состояние гонки возникает, когда два или более потоков доступа к общим данным, для записи, в то же время . (Как работать с условиями гонки, рассматривается далее в этой главе в разделе «Синхронизация ».) Если вы попытаетесь обновить пользовательский интерфейс из другого потока, .NET Framework выдает InvalidOperationException, содержащее следующее сообщение: «Перекрестная резьба операция не valid: Control 'ctrlName', доступ к которому осуществляется из потока, отличного от потока, который был создан . "

Чтобы решить эту проблему, измените метод worker_RunWorkerCompleted следующим образом:

void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
     if (this.InvokeRequired) { 
      this.Invoke(
       new Action(ActionFinish)); 
     } 
     else { 
      ActionFinish(); 
     } 
    } 

В методе worker_RunWorkerCompleted, теперь проверить свойство InvokeRequired . Это свойство определено в классе Control и как таковое присутствует во всех элементах управления на странице. InvokeRequired имеет значение false, если вы вызываете его из потока пользовательского интерфейса и true в противном случае.

Метод Invoke принимает в качестве первого параметра делегат, то есть любой метод может быть размещен там . Новый вызов конструктора Action() используется для обеспечения того, чтобы вы получили делегат. Если у вашего метода есть другая подпись, вы должны соответствующим образом изменить этот конструктор . Остальные параметры метода Invoke отправляются непосредственно на метод , который вы хотите запустить. Invoke помещает вызов метода в очередь, которая будет подхвачена потоком пользовательского интерфейса.

Лучшее решение?

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e { this.Dispatcher.Invoke(()=> //statement); } 

Достаточно вызвать метод Dispatcher.Invoke во всех ситуациях. Этот вызов гарантирует, что оператор lambda expression() => // запускается потоком пользовательского интерфейса, независимо от того, из какого потока вызывается этот метод.

Смежные вопросы