async
Trabalhadores e tarefas assíncronas
Importação
_ <- fat.async
Tipos
A biblioteca async
introduz o tipo Worker
.
Worker
O Worker
é um simples invólucro para uma operação assíncrona.
Construtor
Nome | Assinatura | Breve descrição |
---|---|---|
Worker | (task: Method) | Cria um Worker em modo espera |
O construtor Worker
aceita dois argumentos:
- task: O método a ser executado de forma assíncrona (o método pode não aceitar argumentos diretamente, mas você pode adicionar esses usando duas setas na definição
-> ->
). - wait (opcional): O tempo limite em milissegundos. Se a tarefa não terminar dentro desse tempo, ela será cancelada.
Membros do protótipo
Nome | Assinatura | Breve descrição |
---|---|---|
start | (): Worker | Inicia a tarefa |
cancel | (): Void | Cancela a tarefa |
await | (): Worker | Aguarda pela conclusão da tarefa |
isDone | (): Boolean | Verifica se a tarefa foi concluída |
hasStarted | Boolean | Definido pelo método start |
hasAwaited | Boolean | Definido pelo método await |
isCanceled | Boolean | Definido pelo método cancel |
result | Any | Definido pelo método await |
Métodos avulsos
Nome | Assinatura | Breve descrição |
---|---|---|
atomic | (op: Method): Any | Executa a operação atomicamente |
selfCancel | (): Void | Encerra a execução da thread |
processors | (): Number | Retorna o número de processadores |
Notas de uso
As instâncias de Worker
são mapeadas para threads do sistema em uma base um-para-um e são executadas conforme o agendamento do sistema. Isso implica que sua execução pode nem sempre ser imediata. Para aguardar o resultado de um Worker
, use o método await
.
Diferentemente de outros contextos, no código assíncrono, a task: Method
executa sem acesso ao escopo no qual é criada. Ela só pode acessar propriedades que foram 'curryficadas' -> ->
para dentro do seu escopo de execução ou aquelas que estão diretamente acessíveis no escopo global.
O limite global de memória é compartilhado por todos os Workers
, mas um contexto completamente novo, incluindo uma pilha separada, é alocado para cada um. No entanto, em caso de um erro irrecuperável ou fatal, como a exaustão de memória ou de pilha por um dos Workers
, o interpretador será interrompido e todas as threads serão encerradas.
para manter o máximo desempenho, evite usar interpolação de texto dentro de tarefas assíncronas
Exemplos
async <- fat.async
math <- fat.math
time <- fat.time
# Define uma tarefa lenta
slowTask = (seconds: Number): Text -> -> {
time.wait(seconds * 1000)
'done'
}
# Inicia a tarefa como Worker
worker = Worker(slowTask(5)).start
# Obtém o resultado do worker
result1 = worker.await.result # bloqueia até a tarefa ser concluída
# Inicia uma tarefa com timeout
task = Worker(slowTask(5), 3000).start # a tarefa deve expirar
# Obtém o resultado da tarefa
result2 = task.await.result # bloqueia até a tarefa ser concluída ou ocorrer timeout
o método
await
da gera umAsyncError
se a tarefa exceder o tempo antes da conclusão
atomic
O encapsulador atomic
é uma ferramenta crítica para garantir a segurança das threads e a integridade dos dados na programação concorrente. Quando múltiplos workers ou tarefas assíncronas acessam e modificam recursos compartilhados, condições de corrida podem ocorrer, levando a resultados imprevisíveis e errôneos. A operação atomic
aborda essa questão garantindo que o método que ela envolve seja executado de forma atômica. Isso significa que a operação inteira é completada como uma única unidade indivisível, sem possibilidade de outras threads intervirem no meio do caminho para a mesma operação. Isso é particularmente importante para operações como incrementar um contador, atualizar estruturas de dados compartilhadas ou arquivos, ou realizar qualquer ação onde a ordem de execução importa:
async.atomic(-> file.append(logFile, line))
Embora as operações atomic
sejam uma ferramenta poderosa para garantir a consistência, é importante estar atento ao potencial de contenção que elas introduzem. A contenção ocorre quando múltiplas "threads" ou tarefas tentam executar uma operação simultaneamente, levando a potenciais gargalos de desempenho à medida que cada tarefa espera sua vez. O uso excessivo ou desnecessário de operações atomic
pode degradar significativamente o desempenho de sua aplicação, reduzindo a concorrência. Mantenha apenas a seção crítica de código que absolutamente requer atomicidade envolvida como uma operação atomic
.
por baixo dos panos, operações atômicas são fundamentalmente protegidas por um único
mutex
global
Async na Web Build
Ao usar fry
compilado com Emscripten (por exemplo, ao usar FatScript Playground), o suporte limitado da plataforma para multithreading afeta a implementação do Worker
. Para maximizar a compatibilidade do código entre plataformas, as tarefas do Worker
são executadas inline e bloqueiam a thread principal quando o método start
é chamado. Esta abordagem compromete as vantagens da execução assíncrona, mas permite uma implementação consistente entre plataformas em muitos dos cenários.