Tenho uma pergunta estilística sobre a escolha da implementação do encadeamento em segundo plano que devo usar em um aplicativo de formulário do Windows. Atualmente eu tenho um BackgroundWorker
formulário que possui um (while(true))
loop infinito . Nesse loop, eu uso WaitHandle.WaitAny
para manter o fio cochilando até que algo de interesse aconteça. Um dos manipuladores de eventos que eu espero " StopThread
" é um evento para que eu possa sair do loop. Este evento é sinalizado quando da minha substituição Form.Dispose()
.
Li em algum lugar que BackgroundWorker
é realmente destinado a operações com as quais você não deseja amarrar a interface do usuário e ter um fim finito - como baixar um arquivo ou processar uma sequência de itens. Nesse caso, o "fim" é desconhecido e somente quando a janela é fechada. Portanto, seria mais apropriado usar um Thread de segundo plano em vez de BackgroundWorker
para esse fim?
fonte
CancelAsync
(e testeCancellationPending
se o seu fio será polling em curtos intervalos de tempo, se você quer ter uma exceção gerada em vez disso, use umSystem.Threading.Thread.Abort()
que levanta uma exceção dentro do próprio bloco de rosca, escolher o modelo certo para a situação.Alguns dos meus pensamentos ...
fonte
BackgroundWorker
foi projetado para relatar o progresso do encadeamento a uma parte interessada, que geralmente envolve uma interface do usuário. A documentação do MSDN para a classe deixa isso bem claro. Se você simplesmente precisar que uma tarefa seja executada em segundo plano, prefira usar umThreadPool
encadeamento.System.Windows.Forms
assembléia;BackgroundWorker
também é útil para aplicativos WPF e esses aplicativos podem não ter uma referência ao WinForms.Praticamente o que Matt Davis disse, com os seguintes pontos adicionais:
Para mim, o principal diferencial
BackgroundWorker
é a organização automática do evento concluído viaSynchronizationContext
. Em um contexto de interface do usuário, isso significa que o evento concluído é acionado no thread da interface do usuário e, portanto, pode ser usado para atualizar a interface do usuário. Este é um grande diferencial se você estiver usando oBackgroundWorker
em um contexto de interface do usuário.As tarefas executadas via the
ThreadPool
não podem ser facilmente canceladas (isso incluiThreadPool
.QueueUserWorkItem
E os delegados são executados assincronamente). Portanto, embora evite a sobrecarga de spin-thread, se você precisar de cancelamento, use umBackgroundWorker
ou (mais provavelmente fora da interface do usuário) gire um thread e mantenha uma referência a ele para poder ligarAbort()
.fonte
Além disso, você está vinculando um encadeamento de threads durante a vida útil do trabalhador em segundo plano, o que pode ser motivo de preocupação, pois há apenas um número finito deles. Eu diria que, se você criar o thread apenas uma vez para o seu aplicativo (e não usar nenhum dos recursos do trabalhador em segundo plano), use um thread, em vez de um thread de trabalho em segundo plano / pool de threads.
fonte
Às vezes, é mais fácil trabalhar com um BackgroundWorker, independentemente de você estar usando Windows Forms, WPF ou qualquer outra tecnologia. A parte interessante sobre esses caras é que você trabalha com threads sem ter que se preocupar muito com o local em que o thread está sendo executado, o que é ótimo para tarefas simples.
Antes de usar uma
BackgroundWorker
consideração primeiro, se você deseja cancelar um encadeamento (aplicativo de fechamento, cancelamento do usuário), precisa decidir se o encadeamento deve verificar se há cancelamentos ou se deve ser aplicado na própria execução.BackgroundWorker.CancelAsync()
será definidoCancellationPending
como,true
mas não fará mais nada, é responsabilidade dos threads verificar continuamente isso; lembre-se de que você pode acabar com uma condição de corrida nessa abordagem em que o usuário cancelou, mas o thread foi concluído antes do teste paraCancellationPending
.Thread.Abort()
por outro lado, lançará uma exceção na execução do encadeamento que impõe o cancelamento desse encadeamento, você deve ter cuidado com o que pode ser perigoso, se essa exceção foi repentinamente levantada na execução.O encadeamento precisa de uma consideração muito cuidadosa, independentemente da tarefa, para algumas leituras adicionais:
Programação paralela nas práticas recomendadas de encadeamento gerenciado do .NET Framework
fonte
Eu sabia como usar threads antes de conhecer o .NET, por isso demorou um pouco para me acostumar quando comecei a usar
BackgroundWorker
s. Matt Davis resumiu a diferença com grande excelência, mas eu acrescentaria que é mais difícil compreender exatamente o que o código está fazendo, e isso pode dificultar a depuração. É mais fácil pensar em criar e desligar threads, IMO, do que em dar trabalho a um conjunto de threads.Ainda não posso comentar as postagens de outras pessoas, portanto, perdoe minha claudicação momentânea ao usar uma resposta para lidar com pilares7
Não use
Thread.Abort();
, em vez disso, sinalize um evento e projete seu segmento para terminar normalmente quando sinalizado.Thread.Abort()
gera aThreadAbortException
em um ponto arbitrário na execução do encadeamento, que pode fazer todos os tipos de coisas infelizes, como monitores órfãos, estado compartilhado corrompido e assim por diante.http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx
fonte
Se não está quebrado - conserte até que esteja ... apenas brincando :)
Mas, falando sério, o BackgroundWorker provavelmente é muito parecido com o que você já tem, se você o tivesse iniciado desde o início, talvez tivesse economizado algum tempo - mas neste momento não vejo a necessidade. A menos que algo não esteja funcionando, ou você pense que seu código atual é difícil de entender, então eu continuaria com o que você tem.
fonte
A diferença básica é, como você afirmou, gerando eventos da GUI a partir do
BackgroundWorker
. Se o encadeamento não precisar atualizar a exibição ou gerar eventos para o encadeamento principal da GUI, ele poderá ser um encadeamento simples.fonte
Quero ressaltar um comportamento da classe BackgroundWorker que ainda não foi mencionado. Você pode criar um Thread normal para executar em segundo plano, definindo a propriedade Thread.IsBackground.
Você pode testar esse comportamento chamando o seguinte método no construtor da janela do formulário.
Quando a propriedade IsBackground estiver definida como true e você fechar a janela, seu aplicativo será encerrado normalmente.
Mas quando a propriedade IsBackground estiver definida como false (por padrão) e você fechar a janela, apenas a janela desaparecerá, mas o processo continuará em execução.
A classe BackgroundWorker utiliza um Thread que é executado em segundo plano.
fonte
Um trabalhador em segundo plano é uma classe que trabalha em um encadeamento separado, mas fornece funcionalidade adicional que você não obtém com um encadeamento simples (como manipulação de relatório de progresso da tarefa).
Se você não precisar dos recursos adicionais fornecidos por um trabalhador em segundo plano - e parece que não precisa -, um Thread será mais apropriado.
fonte
O que é desconcertante para mim é que o designer do visual studio permite apenas que você use BackgroundWorkers e Timers que na verdade não funcionam com o projeto de serviço.
Oferece controles de arrastar e soltar em seu serviço, mas ... nem tente implantá-lo. Não vai funcionar.
Serviços: use apenas System.Timers.Timer System.Windows.Forms.Timer não funcionará, mesmo que esteja disponível na caixa de ferramentas
Serviços: BackgroundWorkers não funcionará quando estiver sendo executado como um serviço. Use System.Threading.ThreadPools ou chamadas Async
fonte