O que o novo recurso C# aguarda? [fechado
-
27-09-2019 - |
Pergunta
Alguém pode explicar o que o await
função?
Solução
Eles apenas falei sobre isso no PDC ontem!
Aguarda é usado em conjunto com tarefas (programação paralela) no .NET. É uma palavra -chave que está sendo introduzida na próxima versão do .NET. Mais ou menos, permite que você "pause" a execução de um método para aguardar a tarefa concluir a execução. Aqui está um breve exemplo:
//create and run a new task
Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation);
//run some other code immediately after this task is started and running
ShowLoaderControl();
StartStoryboard();
//this will actually "pause" the code execution until the task completes. It doesn't lock the thread, but rather waits for the result, similar to an async callback
// please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return
dataTask.Start();
DataTable table = await dataTask;
//Now we can perform operations on the Task result, as if we're executing code after the async operation completed
listBoxControl.DataContext = table;
StopStoryboard();
HideLoaderControl();
Outras dicas
Basicamente, o async
e await
Palavras -chave permitem especificar que a execução de um método deve parar em todos os usos de await
, que marcam o método assíncrono chama e depois retomam assim que a operação assíncrona estiver concluída. Isso permite chamar um método no encadeamento principal de um aplicativo e lidar com o trabalho complexo de forma assíncrona, sem a necessidade de definir explicitamente encadeamentos e se unir ou bloquear o thread principal do aplicativo.
Pense nisso como sendo um pouco semelhante a um yield return
declaração em um método que produz um ienumerable. Quando o tempo de execução atinge o yield
, basicamente salvará o estado atual do método e retornará o valor ou referência que está sendo rendido. Na próxima vez que o iEnumerator.MoveNext () for chamado no objeto de retorno (que é gerado internamente pelo tempo de execução), o estado antigo do método é restaurado na pilha e a execução continua com a próxima linha após o yield return
Como se nunca tivéssemos deixado o método. Sem essa palavra-chave, um tipo de iEnumerator deve ser definido sob medida para armazenar o estado e lidar com as solicitações de iteração, com métodos que podem se tornar realmente muito complexos.
Da mesma forma, um método marcado como async
Deve ter pelo menos um await
. Em um await
, o tempo de execução salvará o estado do thread atual e a pilha de chamadas, fará a chamada assíncrona e relaxará de volta ao loop de mensagem do tempo de execução para lidar com a próxima mensagem e manter o aplicativo responsivo. Quando a operação assíncrona é concluída, na próxima oportunidade de agendamento, a pilha de chamadas para aumentar a operação assíncrona é empurrada de volta e continuou como se a chamada fosse síncrona.
Então, essas duas novas palavras -chave simplificam basicamente a codificação de processos assíncronos, como yield return
simplificou a geração de enumeres personalizados. Com algumas palavras-chave e um pouco de conhecimento em segundo plano, você pode pular todos os detalhes confusos e frequentemente propensos a erros de um padrão assíncrono tradicional. Isso será inestimável em praticamente qualquer aplicativo de GUI orientado a eventos como WinForms, WPF do Silverlight.
A resposta atualmente aceita é enganosa.await
não está pausando nada. Primeiro de tudo, ele pode ser usado apenas em métodos ou lambdas marcados como async
e retornando Task
ou void
Se você não se importa com Task
instância em execução neste método.
Aqui está uma ilustração:
internal class Program
{
private static void Main(string[] args)
{
var task = DoWork();
Console.WriteLine("Task status: " + task.Status);
Console.WriteLine("Waiting for ENTER");
Console.ReadLine();
}
private static async Task DoWork()
{
Console.WriteLine("Entered DoWork(). Sleeping 3");
// imitating time consuming code
// in a real-world app this should be inside task,
// so method returns fast
Thread.Sleep(3000);
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("async task iteration " + i);
// imitating time consuming code
Thread.Sleep(1000);
}
});
Console.WriteLine("Exiting DoWork()");
}
}
Resultado:
Entrou dowork (). Dormindo 3
iteração de tarefa assíncrona 0
Status da tarefa: WaitingForAcativation
Esperando por entrar
iteração de tarefa assíncrona 1
iteração de tarefa assíncrona 2
iteração de tarefa assíncrona 3
iteração de tarefa assíncrona 4
iteração de tarefa assíncrona 5
iteração de tarefa assíncrona 6
iteração de tarefa assíncrona 7
iteração de tarefa assíncrona 8
iteração de tarefa assíncrona 9
Saindo dowork ()
Para qualquer pessoa nova em programação assíncrona no .NET, aqui está uma analogia (totalmente falsa) em um cenário com o qual você pode estar mais familiarizado - chamadas de Ajax usando JavaScript/JQuery. Um post simples de jQuery Ajax se parece com o seguinte:
$.post(url, values, function(data) {
// AJAX call completed, do something with returned data here
});
O motivo pelo qual processamos os resultados em uma função de retorno de chamada é, portanto, não bloqueamos o encadeamento atual enquanto aguardamos a chamada do AJAX para retornar. Somente quando a resposta estiver pronta, o retorno de chamada será demitido, liberando o thread atual para fazer outras coisas nesse meio tempo.
Agora, se JavaScript apoiou o await
palavra -chave (o que é claro que nãoainda!)), você pode alcançar o mesmo com isso:
var data = await $.post(url, values);
// AJAX call completed, do something with returned data here
Isso é muito mais limpo, mas com certeza parece que introduzimos o código síncrono e de bloqueio. Mas o compilador (falso) JavaScript teria levado tudo depois await
e conectou -o a um retorno de chamada, então, em tempo de execução, o segundo exemplo se comportaria como o primeiro.
Pode não parecer que está economizando muito trabalho, mas quando se trata de coisas como contextos de manuseio de exceções e sincronização, o compilador está realmente fazendo um muito de levantamento pesado para você. Para mais, eu recomendo o Perguntas frequentes Seguido por Série de blogs de Stephen Cleary.
Se eu tivesse que implementá -lo em Java, seria algo assim:
/**
* @author Ilya Gazman
*/
public abstract class SynchronizedTask{
private ArrayList<Runnable> listeners = new ArrayList<Runnable>();
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000));
public final void await(Runnable listener){
synchronized (this) {
listeners.add(listener);
}
}
public void excecute(){
onExcecute();
for (int i = listeners.size() - 1; i >= 0; i--) {
Runnable runnable;
synchronized (this) {
runnable = listeners.remove(i);
}
threadPoolExecutor.execute(runnable);
}
}
protected abstract void onExcecute();
}
Seu aplicativo usaria assim:
public class Test{
private Job job = new Job();
public Test() {
craeteSomeJobToRunInBackground();
methode1();
methode2();
}
private void methode1(){
System.out.println("Running methode 1");
job.await(new Runnable() {
@Override
public void run() {
System.out.println("Continue to running methode 1");
}
});
}
private void methode2(){
System.out.println("Running methode 2");
}
private void craeteSomeJobToRunInBackground() {
new Thread(new Runnable() {
@Override
public void run() {
job.excecute();
}
}).start();
}
private class Job extends SynchronizedTask{
@Override
protected void onExcecute() {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Job is done");
}
}
}