private void RunAsync()
{
string param = "Hi";
Task.Run(() => MethodWithParameter(param));
}
private void MethodWithParameter(string param)
{
}
Editar
Devido à demanda popular, devo observar que o Task
iniciado será executado em paralelo com o thread de chamada. Assumindo o padrão TaskScheduler
, usará o .NET ThreadPool
. De qualquer forma, isso significa que você precisa levar em conta quaisquer parâmetros que estão sendo passados para o Task
como potencialmente sendo acessados por vários threads de uma vez, tornando-os um estado compartilhado. Isso inclui acessá-los no thread de chamada.
No meu código acima, esse caso é inteiramente discutível. Strings são imutáveis. É por isso que os usei como exemplo. Mas digamos que você não esteja usando um String
...
Uma solução é usar async
e await
. Isso, por padrão, irá capturar o SynchronizationContext
do thread de chamada e criar uma continuação para o resto do método após a chamada await
e anexá-lo ao criado Task
. Se esse método estiver sendo executado no thread da GUI do WinForms, ele será do tipo WindowsFormsSynchronizationContext
.
A continuação será executada após ser postada de volta no capturado SynchronizationContext
- novamente apenas por padrão. Assim, você estará de volta ao tópico com o qual começou após a await
ligação. Você pode alterar isso de várias maneiras, principalmente usando ConfigureAwait
. Em suma, o resto do que o método não continuará até que após a Task
completou em outro segmento. Mas o thread de chamada continuará a ser executado em paralelo, mas não o resto do método.
Essa espera para concluir a execução do resto do método pode ou não ser desejável. Se nada nesse método acessar posteriormente os parâmetros passados para o, Task
você pode não querer mais usar await
.
Ou talvez você use esses parâmetros muito mais tarde no método. Não há razão para isso await
imediatamente, pois você pode continuar fazendo o trabalho com segurança. Lembre-se de que você pode armazenar o Task
retornado em uma variável e await
posteriormente - até mesmo no mesmo método. Por exemplo, uma vez que você precisa acessar os parâmetros passados com segurança depois de fazer um monte de outro trabalho. Novamente, você não precisa estar await
à Task
direita ao executá-lo.
De qualquer forma, uma maneira simples de tornar esse thread-safe com relação aos parâmetros passados para Task.Run
é fazer isso:
Você deve primeiro decorar RunAsync
com async
:
private async void RunAsync()
Nota importante
De preferência, o método marcado não deve retornar void, como a documentação vinculada menciona. A exceção comum são os manipuladores de eventos, como cliques em botões e outros. Eles devem retornar vazios. Caso contrário, sempre tento retornar um ou ao usar . É uma boa prática por vários motivos.async
Task
Task<TResult>
async
Agora você pode await
executar o exemplo Task
abaixo. Você não pode usar await
sem async
.
await Task.Run(() => MethodWithParameter(param));
Portanto, em geral, se você fizer await
a tarefa, poderá evitar tratar os parâmetros passados como um recurso potencialmente compartilhado com todas as armadilhas de modificar algo de vários threads de uma vez. Além disso, tome cuidado com os fechamentos . Não vou cobri-los em profundidade, mas o artigo vinculado faz um ótimo trabalho.
Nota
Um pouco fora do assunto, mas tome cuidado ao usar qualquer tipo de "bloqueio" no thread da GUI do WinForms devido a ele estar marcado com [STAThread]
. O uso await
não bloqueia de forma alguma, mas às vezes vejo que é usado em conjunto com algum tipo de bloqueio.
"Block" está entre aspas porque você tecnicamente não pode bloquear o thread da GUI do WinForms . Sim, se você usar lock
o thread da GUI do WinForms, ele ainda enviará mensagens, apesar de você pensar que está "bloqueado". Não é.
Isso pode causar problemas bizarros em casos muito raros. Uma das razões pelas quais você nunca quer usar um lock
ao pintar, por exemplo. Mas esse é um caso marginal e complexo; no entanto, eu já vi isso causar problemas malucos. Então, eu anotei isso por uma questão de integridade.