Eu pensei que eles eram basicamente a mesma coisa - escrever programas que dividem tarefas entre processadores (em máquinas com mais de 2 processadores). Então eu estou lendo isso , que diz:
Os métodos assíncronos devem ser operações sem bloqueio. Uma expressão de espera em um método assíncrono não bloqueia o encadeamento atual enquanto a tarefa aguardada está em execução. Em vez disso, a expressão inscreve o restante do método como uma continuação e retorna o controle para o chamador do método assíncrono.
As palavras-chave assíncronas e aguardam não fazem com que threads adicionais sejam criados. Os métodos assíncronos não requerem multithreading porque um método assíncrono não é executado em seu próprio encadeamento. O método é executado no contexto de sincronização atual e usa o tempo no encadeamento apenas quando o método está ativo. Você pode usar o Task.Run para mover o trabalho vinculado à CPU para um encadeamento em segundo plano, mas um encadeamento em segundo plano não ajuda em um processo que está apenas aguardando a disponibilização dos resultados.
e estou pensando se alguém pode traduzir isso para o inglês para mim. Parece fazer uma distinção entre assincronicidade (isso é uma palavra?) E encadeamento e implica que você pode ter um programa que possui tarefas assíncronas, mas sem multithreading.
Agora entendo a ideia de tarefas assíncronas, como o exemplo na pág. 467 de C # de Jon Skeet em profundidade, terceira edição
async void DisplayWebsiteLength ( object sender, EventArgs e )
{
label.Text = "Fetching ...";
using ( HttpClient client = new HttpClient() )
{
Task<string> task = client.GetStringAsync("http://csharpindepth.com");
string text = await task;
label.Text = text.Length.ToString();
}
}
A async
palavra-chave significa " Esta função, sempre que for chamada, não será chamada em um contexto em que sua conclusão seja necessária para que tudo após a chamada seja chamada".
Em outras palavras, escrevê-lo no meio de alguma tarefa
int x = 5;
DisplayWebsiteLength();
double y = Math.Pow((double)x,2000.0);
, já DisplayWebsiteLength()
que não tem nada a ver com x
ou y
, fará com DisplayWebsiteLength()
que seja executado "em segundo plano", como
processor 1 | processor 2
-------------------------------------------------------------------
int x = 5; | DisplayWebsiteLength()
double y = Math.Pow((double)x,2000.0); |
Obviamente, esse é um exemplo estúpido, mas estou correto ou totalmente confuso ou o quê?
(Além disso, estou confuso sobre o porquê sender
e e
nunca é usado no corpo da função acima.)
fonte
sender
ee
estão sugerindo que esse é realmente um manipulador de eventos - praticamente o único lugar ondeasync void
é desejável. Provavelmente, isso é chamado com o clique de um botão ou algo parecido - o resultado é que essa ação ocorre completamente de forma assíncrona em relação ao restante do aplicativo. Mas ainda está tudo em um encadeamento - o encadeamento da interface do usuário (com um pequeno intervalo de tempo em um encadeamento IOCP que lança o retorno de chamada no encadeamento da interface do usuário).DisplayWebsiteLength
exemplo de código: Você não deve usarHttpClient
em umausing
instrução - Sob uma carga pesada, o código pode esgotar o número de soquetes disponíveis, resultando em erros de SocketException. Mais informações sobre instanciação imprópria .Respostas:
Seu mal-entendido é extremamente comum. Muitas pessoas aprendem que multithreading e assincronia são a mesma coisa, mas não são.
Uma analogia geralmente ajuda. Você está cozinhando em um restaurante. Uma encomenda chega para ovos e torradas.
Agora, faz sentido que o multithreading seja apenas um tipo de assincronia? Enfiar é sobre trabalhadores; assincronia é sobre tarefas . Nos fluxos de trabalho multithread, você atribui tarefas aos trabalhadores. Nos fluxos de trabalho assíncronos de thread único, você tem um gráfico de tarefas em que algumas tarefas dependem dos resultados de outras; à medida que cada tarefa é concluída, ele invoca o código que agenda a próxima tarefa que pode ser executada, dados os resultados da tarefa recém-concluída. Mas você (espero) precisa apenas de um trabalhador para executar todas as tarefas, não de um trabalhador por tarefa.
Isso ajudará a perceber que muitas tarefas não são vinculadas ao processador. Para tarefas vinculadas ao processador, faz sentido contratar tantos trabalhadores (threads) quantos houver processadores, atribuir uma tarefa a cada trabalhador, atribuir um processador a cada trabalhador e fazer com que cada processador faça o trabalho de nada além de calcular o resultado como o mais rápido possível. Mas para tarefas que não estão aguardando em um processador, você não precisa atribuir um trabalhador. Você apenas espera a chegada da mensagem de que o resultado está disponível e faz outra coisa enquanto espera . Quando essa mensagem chegar, você poderá agendar a continuação da tarefa concluída como a próxima coisa na lista de tarefas a ser desmarcada.
Então, vejamos o exemplo de Jon com mais detalhes. O que acontece?
text
e executar o resto do método.É como na minha analogia. Alguém pede um documento. Você envia pelo correio o documento e continua fazendo outro trabalho. Quando chega no correio, você recebe um sinal e, quando lhe apetece, faz o resto do fluxo de trabalho - abra o envelope, pague as taxas de entrega, qualquer que seja. Você não precisa contratar outro trabalhador para fazer tudo isso por você.
fonte
Javascript no navegador é um ótimo exemplo de um programa assíncrono que não possui threads.
Você não precisa se preocupar com vários trechos de código que tocam nos mesmos objetos ao mesmo tempo: cada função termina a execução antes que qualquer outro javascript possa ser executado na página.
No entanto, ao fazer algo como uma solicitação AJAX, nenhum código está sendo executado; portanto, outro javascript pode responder a coisas como eventos de clique até que a solicitação volte e invoque o retorno de chamada associado a ela. Se um desses outros manipuladores de eventos ainda estiver em execução quando a solicitação AJAX voltar, seu manipulador não será chamado até que seja concluído. Existe apenas um "encadeamento" de JavaScript em execução, embora seja possível pausar efetivamente o que estava fazendo até que você tenha as informações necessárias.
Nos aplicativos C #, o mesmo acontece sempre que você lida com elementos da interface do usuário - você só pode interagir com elementos da interface do usuário quando estiver no thread da interface do usuário. Se o usuário clicar em um botão e você desejar responder lendo um arquivo grande do disco, um programador inexperiente poderá cometer o erro de ler o arquivo no próprio manipulador de eventos click, o que levaria o aplicativo a "congelar" até o o carregamento do arquivo terminou porque não é permitido responder a mais eventos de cliques, pairar ou outros relacionados à interface do usuário até que esse segmento seja liberado.
Uma opção que os programadores podem usar para evitar esse problema é criar um novo encadeamento para carregar o arquivo e informar ao código desse encadeamento que, quando o arquivo é carregado, ele precisa executar o código restante no encadeamento da interface do usuário novamente para atualizar os elementos da interface do usuário. com base no que foi encontrado no arquivo. Até recentemente, essa abordagem era muito popular porque era o que as bibliotecas e a linguagem C # facilitavam, mas é fundamentalmente mais complicado do que precisa ser.
Se você pensar no que a CPU está fazendo quando lê um arquivo no nível do hardware e do sistema operacional, está basicamente emitindo uma instrução para ler partes de dados do disco na memória e atingir o sistema operacional com uma "interrupção". "quando a leitura estiver concluída. Em outras palavras, a leitura do disco (ou de qualquer E / S realmente) é uma operação inerentemente assíncrona . O conceito de um encadeamento aguardando a conclusão da E / S é uma abstração criada pelos desenvolvedores da biblioteca para facilitar a programação. Não é necessário.
Agora, a maioria das operações de E / S no .NET possui um
...Async()
método correspondente que você pode chamar, que retorna umTask
quase imediatamente. Você pode adicionar retornos de chamada a issoTask
para especificar o código que você deseja executar quando a operação assíncrona for concluída. Você também pode especificar em qual encadeamento deseja executar esse código e fornecer um token no qual a operação assíncrona pode verificar periodicamente para ver se você decidiu cancelar a tarefa assíncrona, dando a oportunidade de interromper seu trabalho rapidamente e graciosamente.Até as
async/await
palavras - chave serem adicionadas, o C # era muito mais óbvio sobre como o código de retorno de chamada é chamado, porque esses retornos estavam na forma de delegados que você associou à tarefa. Para ainda oferecer o benefício de usar a...Async()
operação, evitando complexidade no código,async/await
abstrai a criação desses delegados. Mas eles ainda estão lá no código compilado.Assim, você pode fazer com que o manipulador de eventos da interface do usuário seja
await
uma operação de E / S, liberando o encadeamento da interface do usuário para fazer outras coisas e retornando mais ou menos automaticamente ao encadeamento da interface do usuário quando terminar de ler o arquivo - sem precisar crie um novo thread.fonte