Entenda completamente o que são síncronos e assíncronos em um artigo!

Acredito que quando muitos estudantes encontram as duas palavras síncrono e assíncrono, seus cérebros caem instantaneamente em um estado de confusão, como uma encruzilhada onde o semáforo falha:

Sim, essas duas palavras que parecem muito semelhantes, mas na verdade são muito semelhantes, causaram muitos problemas para os blogueiros. Qual é o significado por trás dessas duas palavras?

Vamos começar com a cena do trabalho.

Programador trabalhador

Suponha agora que seu chefe lhe atribuiu uma tarefa muito urgente e importante que você deve concluir antes de sair do trabalho (capitalismo maligno). Para supervisionar o progresso, o chefe moveu uma cadeira e sentou-se de lado para observar você escrever o código. Você deve estar xingando em sua mente: "WTF, você tem tanto tempo livre? Você não pode fazer outras coisas enquanto olha para mim? " O chefe parecia ter recebido suas ondas cerebrais: "Estou apenas esperando aqui ... Não vou a lugar nenhum, nem ao banheiro, até você terminar de escrever.

Neste exemplo, o chefe fica esperando depois de lhe dar a tarefa e não faz nada até você terminar de escrever . Este cenário é chamado de sincronização. No dia seguinte, seu chefe lhe dá outra tarefa. Mas desta vez eu não estava tão ansioso. Desta vez, o chefe disse levemente: "Jovem, sim, nada mal. Se você trabalhar duro por mais um ano, estarei financeiramente livre no próximo ano. Não há pressa para a tarefa de hoje. Apenas me avise quando terminar de escrevê-lo." ". Desta vez, o chefe não observou você escrevendo o código, mas se virou para assistir ao vídeo. Depois de terminar de escrever, você simplesmente informou ao chefe "Terminei".

Neste exemplo, depois que o chefe conclui a tarefa, ele não espera mais e não faz nada, mas passa a trabalhar em outras coisas. Depois de concluir a tarefa, basta informar ao chefe que a tarefa foi concluída. Isso é chamado de assíncrono. É importante notar que em um cenário assíncrono, o foco é que seu chefe esteja assistindo ao programa enquanto você escreve o código. As duas coisas estão acontecendo ao mesmo tempo , em vez de uma parte esperar pela outra , então é por isso que assíncrono geralmente é mais eficiente do que síncrono. A essência disso é que , independentemente do cenário, os aplicativos síncronos e assíncronos são usados. Podemos ver que a palavra sincronização está frequentemente relacionada às palavras-chave como "dependência", "associado" e "espera" da tarefa, enquanto assíncrona está frequentemente relacionada às palavras-chave da tarefa como "não dependente", " não relacionado", "não há necessidade de esperar" e "simultaneamente". Palavras-chave relacionadas como "ocorrência". A propósito, se você encontrar um chefe que está observando você escrever código por trás, Trinta e Seis Estratégias é a melhor estratégia.

Faça ligações e envie e-mails

Como um programador trabalhador, você não pode simplesmente mergulhar na construção de tijolos. A comunicação no trabalho diário não pode ser evitada. Uma das formas mais eficientes de comunicação é a briga. . . Ah não, é o telefone.

Normalmente, ao fazer uma ligação, uma pessoa está falando e a outra está ouvindo. Enquanto uma pessoa está falando, a outra está esperando e continua depois que a outra pessoa termina de falar. Portanto, nesta cena, você pode ver que "dependência" Palavras-chave como "correlação" e "espera" aparecem, portanto o método de comunicação para fazer ligações é a chamada sincronização.

Outro método de comunicação comum entre codificadores é o email. O e-mail é outra forma de comunicação essencial, porque ninguém está esperando que você escreva um e-mail e não faça nada, então você pode escrever devagar e sem pressa, e enquanto você escreve o e-mail, o destinatário pode fazer algo como pescar. ir ao banheiro e, ao mesmo tempo, reclamar de coisas significativas, como por que não há duas semanas de feriado do Dia Nacional. Ao mesmo tempo, depois de terminar de escrever e enviar o e-mail, você não precisa esperar que a outra parte responda e não faça nada. Você também pode fazer algumas coisas significativas, como pescar.

Aqui, você escreve um e-mail e outras pessoas aproveitam isso. As duas coisas acontecem ao mesmo tempo. Nem o destinatário nem o remetente precisam esperar um pelo outro. Quando o remetente terminar de escrever o e-mail, ele pode simplesmente clicar em enviar O remetente pode lê-lo depois de recebê-lo. O destinatário e o remetente não precisam confiar um no outro ou esperar um pelo outro. Você vê, neste cenário, as palavras-chave “não dependente”, “não relacionado” e “não há necessidade de esperar” aparecem, portanto o método de comunicação por e-mail é assíncrono.

Chamada síncrona

Agora, finalmente, de volta ao tópico da programação. Agora que entendemos o significado de sincronização e assíncrona em vários cenários (espero que sim), como os programadores devem entender a sincronização e assíncrona? Vamos falar primeiro sobre chamadas síncronas, que é o cenário mais familiar aos programadores. As chamadas de função gerais são síncronas, assim:

funcA() {
// 等待函数funcB执行完成
    funcB();

// 继续接下来的流程
}

funcA chama funcB, então o código subsequente em funcA não será executado até que funcB conclua a execução, o que significa que funcA deve esperar que funcB conclua a execução, assim:

Na imagem acima, podemos ver que funcA não pode fazer nada enquanto funcB está em execução, esta é uma sincronização típica.

Observe que, de modo geral, para chamadas síncronas como essa, funcA e funcB são executadas no mesmo thread, que é a situação mais comum. Mas é importante notar que mesmo funções executadas em dois threads diferentes podem ser chamadas de forma síncrona.Quando realizamos operações de IO, a camada inferior na verdade faz solicitações ao sistema operacional por meio de chamadas de sistema, como leitura de arquivos de disco:

read(file, buf);

Isso está bloqueando a E / S. O programa não pode continuar avançando antes que a função de leitura retorne.

read(file, buf);
// 程序暂停运行,
// 等待文件读取完成后继续运行

como mostra a imagem:

Somente após o retorno da função de leitura o programa pode continuar a execução. Observe que, diferentemente da chamada síncrona acima, a função e a função chamada são executadas em threads diferentes. Portanto, podemos concluir que as chamadas síncronas não têm nada a ver com o fato de a função e a função chamada serem executadas no mesmo thread . Gostaríamos aqui de enfatizar novamente que a função e a função chamada não podem ser executadas ao mesmo tempo no modo síncrono. A programação síncrona é a mais natural e fácil de entender para os programadores. Mas o preço fácil de entender é que, em alguns cenários, a sincronização não é eficiente. A razão é simples porque as tarefas não podem ser executadas ao mesmo tempo . A seguir, veremos as chamadas assíncronas.

   Information Direct: rota de aprendizado da tecnologia de código-fonte do kernel Linux + tutorial em vídeo do código-fonte do kernel

Learning Express: Código-fonte do kernel Linux Ajuste de memória Sistema de arquivos Gerenciamento de processos Driver de dispositivo/pilha de protocolo de rede

chamada assíncrona

Existem chamadas síncronas e chamadas assíncronas. Se você realmente entendeu o que li nesta seção até agora, as chamadas assíncronas não serão um problema para você. De modo geral, as chamadas assíncronas sempre andam de mãos dadas com tarefas demoradas , como operações de E/S , como leitura e gravação de arquivos em disco, envio e recebimento de dados de rede, operações de banco de dados, etc. Ainda vamos usar a leitura de arquivos em disco como exemplo. No modo de chamada síncrona da função de leitura, o chamador não pode continuar avançando antes que o arquivo seja lido, mas a situação é diferente se a função de leitura puder ser chamada de forma assíncrona. Se a função de leitura puder ser chamada de forma assíncrona, a função de leitura poderá retornar imediatamente, mesmo que o arquivo não tenha sido lido.

read(file, buff);
// read函数立即返回
// 不会阻塞当前程序

assim:

Pode-se observar que no método de chamada assíncrona, o chamador não será bloqueado e o próximo programa poderá ser executado imediatamente após a conclusão da chamada da função. O ponto-chave do assíncrono neste momento é que a execução subsequente do programa do chamador pode ser realizada ao mesmo tempo que a leitura do arquivo . Também podemos ver isso na figura acima. Esta é a eficiência do assíncrono. No entanto, observe que as chamadas assíncronas são um fardo para os programadores em termos de compreensão, e ainda mais um fardo na escrita de código. Em geral, quando Deus abre uma porta para você, ele a fecha apropriadamente. Alguns alunos podem perguntar, em uma chamada síncrona, o chamador não continua mais a execução, mas faz uma pausa e espera. Depois que a função chamada é executada, é natural que o chamador continue a execução. Portanto, em uma chamada assíncrona, como o chamador sabe se a função chamada é A execução foi concluída? Isso é dividido em duas situações:

  1. O chamador não se importa com os resultados da execução
  2. O chamador precisa saber o resultado da execução

O primeiro caso é relativamente simples e não requer discussão. O segundo caso é mais interessante. Geralmente existem dois métodos de implementação: um é o mecanismo de notificação, o que significa que quando a execução da tarefa é concluída, um sinal é enviado para notificar o chamador de que a tarefa foi concluída. Observe que existem muitas implementações métodos para o sinal aqui.Linux signal in ou usando mecanismos como semáforos. O outro é o retorno de chamada, que costumamos chamar de retorno de chamada. Vamos nos concentrar no retorno de chamada no próximo artigo e haverá uma breve discussão neste artigo. A seguir, usamos um exemplo específico para explicar chamadas síncronas e chamadas assíncronas.

Síncrono VS Assíncrono

Tomamos um serviço Web comum como exemplo para ilustrar esse problema. De um modo geral, o servidor Web terá alguma lógica de processamento típica após receber a solicitação do usuário. A mais comum é a consulta ao banco de dados (claro, você também pode substituir a consulta ao banco de dados aqui por outras operações de E/S, como leitura de disco e rede comunicação. etc.), aqui assumimos que o processamento de uma solicitação do usuário requer passar pelas etapas A, B e C e, em seguida, ler o banco de dados. Após a leitura do banco de dados ser concluída, ele precisa passar pelas etapas D, E e F , assim:

# 处理一次用户请求需要经过的步骤:
A;
B;
C;
数据库读取;
D;
E;
F;

As etapas A, B, C e D, E, F não requerem nenhuma E/S, o que significa que essas seis etapas não requerem leitura de arquivos, comunicação de rede, etc. Somente a etapa de consulta ao banco de dados envolve operações de E/S. De modo geral, esse servidor Web possui dois threads típicos: o thread principal e o thread de processamento do banco de dados. Observe que esta discussão é apenas um cenário típico. O negócio específico pode realmente ser diferente, mas isso não afeta o uso de dois threads. para ilustrar o problema. Primeiro, vejamos a implementação mais simples, que é a sincronização. Este método é o mais natural e fácil de entender:

// 主线程
main_thread() {
    A;
    B;
    C;
    发送数据库查询请求;
    D;
    E;
    F;
}
// 数据库线程
DataBase_thread() {
while(1) {
        处理数据库读取请求;
        返回结果;
    }
}

Este é o método de sincronização mais típico. Depois de emitir uma solicitação de consulta ao banco de dados, o thread principal será bloqueado e suspenso até que a consulta ao banco de dados seja concluída. D, E e F podem continuar a ser executados, assim:

Pela imagem, podemos ver que haverá uma “lacuna” no thread principal. Essa lacuna é o “tempo de lazer” do thread principal. Durante esse tempo de lazer, o thread principal precisa aguardar a conclusão do banco de dados consulta antes de continuar o processo de processamento subsequente. Aqui o thread principal é como o chefe que supervisiona o trabalho, e o thread do banco de dados é como o programador que trabalha duro para mover os tijolos. O chefe não faz nada até que os tijolos sejam movidos, apenas olha para você de perto e espera que você termine de mover os tijolos antes de passar para outras coisas. Obviamente, programadores eficientes não podem tolerar que o thread principal seja preguiçoso . É hora de usar a grande arma, que é assíncrona. No esquema de implementação assíncrona, o thread principal não espera que a consulta ao banco de dados seja concluída, mas processa diretamente a próxima solicitação após enviar a solicitação de leitura e gravação do banco de dados. Alguns alunos podem ter dúvidas. Uma solicitação precisa passar por sete etapas: A, B, C, consulta ao banco de dados, D, E e F. Se o thread principal processar diretamente a próxima etapa após concluir A, B, C e banco de dados query.request, e as etapas restantes D, E e F da solicitação anterior? Se você não esqueceu o conteúdo da seção anterior, saiba que existem duas situações, vamos discuti-las separadamente.

1. O thread principal não se importa com os resultados da operação do banco de dados . Nesse caso, o thread principal não se importa se a consulta ao banco de dados foi concluída. Após a conclusão da consulta ao banco de dados, ele processará as próximas três etapas D, E , e F sozinho, assim:

Veja, aqui vem o ponto chave.

Dissemos que uma solicitação precisa passar por sete etapas, das quais as três primeiras são concluídas no thread principal e as quatro últimas são concluídas no thread do banco de dados. Então, como o thread do banco de dados sabe processar D, E, quais são? esses passos? Neste momento, nossa outra função de retorno de chamada protagonista começa a aparecer. Sim, a função de retorno de chamada é usada para resolver este problema. Podemos encapsular as etapas de processamento de D, E e F em uma função. Suponha que a função seja chamada handle_DEF_after_DB_query:

void handle_DEF_after_DB_query () {
    D;
    E;
    F;
}

Desta forma, a thread principal passará a função como parâmetro ao enviar a solicitação de consulta ao banco de dados :

DB_query(request, handle_DEF_after_DB_query);

Após o processamento do thread do banco de dados, basta chamar handle_DEF_after_DB_query diretamente.Esta é a função da função de retorno de chamada. Alguns alunos também podem ter dúvidas, por que essa função deveria ser passada para a thread do banco de dados em vez de ser definida e chamada pela própria thread do banco de dados? Porque do ponto de vista da estrutura organizacional do software, esse não é o trabalho que o thread do banco de dados deveria fazer . Tudo o que o encadeamento do banco de dados precisa fazer é consultar o banco de dados e, em seguida, chamar uma função de processamento. Quanto ao que essa função de processamento faz, o encadeamento do banco de dados não se importa nem um pouco, e não deveria se importar . Você pode passar várias funções de retorno de chamada. Em outras palavras, o sistema de banco de dados pode ser programado para a variável de função abstrata da função de retorno de chamada para lidar melhor com as mudanças, porque a mudança no conteúdo da função de retorno de chamada não afetará a lógica do thread do banco de dados, e se o thread do banco de dados define sua própria função de processamento, então não há flexibilidade neste design. Do ponto de vista do desenvolvimento de software, supondo que a lógica do thread do banco de dados seja encapsulada e fornecida a outras equipes para bibliotecas, como a equipe do banco de dados pode saber o que fazer após uma consulta ao banco de dados durante o desenvolvimento? Obviamente, apenas o usuário sabe o que fazer após consultar o banco de dados, então o usuário pode simplesmente passar essa função de retorno de chamada ao usá-lo. Dessa forma, a equipe complexa do banco de dados consegue a chamada dissociação da equipe do usuário . Agora você deve entender a função da função de retorno de chamada.

Além disso, observe atentamente as duas imagens acima. Você consegue ver por que o assíncrono é mais eficiente do que o síncrono? A razão é muito simples, que é a que mencionamos neste artigo: a assincronia naturalmente não requer espera nem dependências. Na imagem anterior, podemos ver que o "tempo de lazer" do thread principal acabou, substituído por trabalho constante, trabalho, trabalho, assim como os programadores 996 trabalhadores, e o thread do banco de dados não é tão longo. é substituído por trabalho, trabalho, trabalho.

As solicitações de processamento do thread principal e as solicitações de consulta de processamento do banco de dados podem ser realizadas ao mesmo tempo . Portanto, do ponto de vista do desempenho do sistema, esse design pode fazer uso mais completo dos recursos do sistema e processar solicitações mais rapidamente; do ponto de vista do usuário, o sistema a resposta também será mais rápida. Esta é a eficiência do assíncrono. Mas também deveríamos ser capazes de ver que a programação assíncrona não é tão fácil de entender quanto a síncrona, e a capacidade de manutenção do sistema não é tão boa quanto o modo síncrono. Então, existe uma maneira de combinar a facilidade de compreensão do modo síncrono com a eficiência do modo assíncrono? A resposta é sim, e explicaremos essa tecnologia detalhadamente nos capítulos subsequentes. A seguir, vejamos a segunda situação, ou seja, o thread principal precisa se preocupar com os resultados da consulta ao banco de dados.

2. O thread principal se preocupa com os resultados da operação do banco de dados . Nesse caso, o thread do banco de dados precisa enviar os resultados da consulta ao thread principal usando o mecanismo de notificação. Depois de receber a mensagem, o thread principal continua a processar a segunda metade do solicitação anterior, assim:

A partir daqui podemos ver que as etapas do ABCDEF são todas processadas no thread principal. Ao mesmo tempo, o thread principal também não tem "tempo de lazer", mas neste caso o thread do banco de dados é relativamente lento. Não existe um método anterior aqui. O método é eficiente, mas ainda é mais eficiente que o modo síncrono. Por fim, deve-se notar que o assíncrono não é necessariamente mais eficiente que o síncrono em todos os casos, mas também precisa ser analisado com base no negócio específico e na complexidade do IO.

Resumir

Neste artigo, analisamos os dois conceitos de sincronização e assincronidade em vários cenários, mas não importa qual seja o cenário, a sincronização muitas vezes significa que ambas as partes têm que esperar uma pela outra e depender uma da outra, enquanto assíncrona significa que ambas as partes são independentes uns dos outros e fazer suas próprias coisas.

Autor original: Sobrevivência na Ilha Deserta de Coder

Acho que você gosta

Origin blog.csdn.net/youzhangjing_/article/details/132739158
Recomendado
Clasificación