Eu tenho o método:
private static void Method()
{
Console.WriteLine("Method() started");
for (var i = 0; i < 20; i++)
{
Console.WriteLine("Method() Counter = " + i);
Thread.Sleep(500);
}
Console.WriteLine("Method() finished");
}
E eu quero iniciar esse método em uma nova tarefa. Eu posso começar uma nova tarefa como esta
var task = Task.Factory.StartNew(new Action(Method));
ou isto
var task = Task.Run(new Action(Method));
Mas existe alguma diferença entre Task.Run()
e Task.Factory.StartNew()
. Ambos estão usando ThreadPool e iniciam Method () imediatamente após a criação da instância da tarefa. Quando devemos usar a primeira variante e quando a segunda?
c#
multithreading
task-parallel-library
Sergiy Lichenko
fonte
fonte
StartNew
por padrão, usosTaskScheduler.Current
que podem ser o pool de threads, mas também podem ser o thread da interface do usuário.Respostas:
O segundo método,
Task.Run
foi introduzido em uma versão posterior da estrutura .NET (no .NET 4.5).No entanto, o primeiro método
Task.Factory.StartNew
,, oferece a oportunidade de definir muitas coisas úteis sobre o encadeamento que você deseja criar, enquantoTask.Run
não fornece isso.Por exemplo, digamos que você deseja criar um encadeamento de tarefas de execução longa. Se um encadeamento do conjunto de encadeamentos for usado para esta tarefa, isso poderá ser considerado um abuso do conjunto de encadeamentos.
Uma coisa que você poderia fazer para evitar isso seria executar a tarefa em um thread separado. Um encadeamento recém-criado que seria dedicado a esta tarefa e seria destruído assim que sua tarefa fosse concluída. Você não pode conseguir isso com o
Task.Run
, enquanto pode fazê-lo com oTask.Factory.StartNew
, como abaixo:Como se afirma aqui :
fonte
that’s exactly equivalent to
não é válido.tha's exactly equivalent to
não contém. Desde já, obrigado. Seria bom explicar com comentários no seu código. Obrigado :)TaskScheduler.Default
. Consulte aqui o sourcesource.microsoft.com/#mscorlib/system/threading/Tasks/… .As pessoas já mencionaram isso
É equivalente a
Mas ninguém mencionou isso
É equivalente a:
Como você pode ver, dois parâmetros são diferentes para
Task.Run
eTask.Factory.StartNew
:TaskCreationOptions
-Task.Run
usos, oTaskCreationOptions.DenyChildAttach
que significa que as tarefas filho não podem ser anexadas ao pai, considere o seguinte:Quando invocamos
parentTask.Wait()
,childTask
não será aguardado, mesmo que tenhamos especificadoTaskCreationOptions.AttachedToParent
isso, porque issoTaskCreationOptions.DenyChildAttach
proíbe que crianças se apeguem a ele. Se você executar o mesmo código emTask.Factory.StartNew
vez deTask.Run
,parentTask.Wait()
esperaráchildTask
porqueTask.Factory.StartNew
usaTaskCreationOptions.None
TaskScheduler
-Task.Run
usa, oTaskScheduler.Default
que significa que o agendador de tarefas padrão (aquele que executa tarefas no Pool de Threads) sempre será usado para executar tarefas.Task.Factory.StartNew
por outro lado, usa oTaskScheduler.Current
que significa planejador do encadeamento atual, pode ser,TaskScheduler.Default
mas nem sempre. Na verdade, quando o desenvolvimentoWinforms
ouWPF
aplicações, é necessário para atualização UI do segmento atual, para fazer isso as pessoas usamTaskScheduler.FromCurrentSynchronizationContext()
agendador de tarefas, se você acidentalmente criar outra tarefa longa em execução dentro tarefa que costumavaTaskScheduler.FromCurrentSynchronizationContext()
programador da interface do usuário será congelado. Uma explicação mais detalhada disso pode ser encontrada aquiPortanto, geralmente, se você não estiver usando a tarefa filho aninhada e sempre desejar que suas tarefas sejam executadas no Conjunto de Encadeamentos, é melhor usá-lo
Task.Run
, a menos que você tenha alguns cenários mais complexos.fonte
Veja este artigo do blog que descreve a diferença. Basicamente fazendo:
É o mesmo que fazer:
fonte
Foi
Task.Run
introduzido na versão mais recente do .NET framework e é recomendado .O
Task.Factory.StartNew
tem mais opções, oTask.Run
é uma abreviação:Por atalho, quero dizer um atalho técnico :
fonte
De acordo com este post de Stephen Cleary, Task.Factory.StartNew () é perigoso:
Aqui estão os motivos reais:
Isso significa que ele pode ser executado potencialmente no thread da interface do usuário se uma operação for concluída e ele retornará ao thread da interface do usuário devido a uma continuação, como Stephen Cleary explica mais detalhadamente em sua postagem.
No meu caso, eu estava tentando executar tarefas em segundo plano ao carregar um datagrid para uma exibição enquanto exibia uma animação ocupada. A animação ocupada não foi exibida ao usar,
Task.Factory.StartNew()
mas foi exibida corretamente quando mudei paraTask.Run()
.Para obter detalhes, consulte https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
fonte
Além das semelhanças, ou seja, Task.Run () é uma abreviação de Task.Factory.StartNew (), há uma pequena diferença entre o comportamento deles no caso de delegados de sincronização e assíncrona.
Suponha que existem dois métodos a seguir:
Agora considere o seguinte código.
Aqui, sync1 e sync2 são do tipo Task <int>
No entanto, a diferença ocorre no caso de métodos assíncronos.
Nesse cenário, async1 é do tipo Tarefa <int>, mas async2 é do tipo Tarefa <Tarefa <int>>
fonte
Task.Run
incorporou a funcionalidade de desempacotamento doUnwrap
método. Aqui está um post do blog que explica o raciocínio por trás dessa decisão.Na minha aplicação que chama dois serviços, comparei Task.Run e Task.Factory.StartNew . Descobri que, no meu caso, os dois funcionam bem. No entanto, o segundo é mais rápido.
fonte