Tento me familiarizar com a simultaneidade do Swift, especialmente com o MainActor.
Eu criei para mim esta classe de demonstração :
@MainActor
class ThreadsDemo {
let range1000 = 0.. < 1000
var randomNumber = 0
init() {
randomNumber = Int.random(in: range1000)
}
func modifyRandomNumber() async {
print("2. isMain: \(Thread.isMainThread)")
let newRandomNumber = Int.random(in: range1000)
print("Generated random-number -> \(newRandomNumber)")
Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) {
_ in
print("4. isMain (within wait): \(Thread.isMainThread)")
}
print("5. isMain: \(Thread.isMainThread)")
randomNumber = newRandomNumber
}
}
Invocando o método ThreadDemo:
.task {
print("1. isMain: \(Thread.isMainThread)")
print("Initial random-number -> \(threadsDemo.randomNumber)")
await threadsDemo.modifyRandomNumber()
print("Modified random-number -> \(threadsDemo.randomNumber)")
print("6. isMain: \(Thread.isMainThread)")
}
Resultado:
1. isMain: true
Initial random-number -> 455
2. isMain: true
Generated random-number -> 578
5. isMain: true
Modified random-number -> 578
6. isMain: true
4. isMain (within wait): true
Obviamente executa tudo no thread principal, até mesmo o bloco dentro do temporizador agendado.
Estou confuso, porque pensei que ele usaria threads em segundo plano para evitar que a interface do usuário travasse e parasse de responder.
Se ele executa tudo no thread principal de qualquer maneira, então qual é o propósito de usar tarefas?
Mas o que eu realmente tentei descobrir:
A anotação @MainActor resulta na execução de tudo no thread principal (todo o código executável) ou resulta apenas em fazer alterações no estado (atribuições, etc.), sempre no thread principal?
A
Task
ou.task { ... }
permite que você execute código de forma assíncrona . Permite chamarasync
métodos eawait
coisas do tipo. Não precisa estar em outra thread. Nesse caso, todos os métodos que você está chamando durante o.task { ... }
evento são isolados pelo ator principal, então todos são executados na thread principal.Se você chamar um
async
método não isolado ou um método isolado para um ator diferente, o código nesse método será executado em um thread diferente.Pode-se argumentar que todo código altera algum estado (o contador do programa, pelo menos ), então as duas opções que você mencionou não são realmente distinguíveis.
Um método isolado do ator principal faz com que todas as partes síncronas do seu corpo sejam executadas na thread principal, ou seja, as partes onde você não precisa
await
. Uma vez que você defineawait
algo, a thread em que o código será executado depende desse "algo". Se também estiver isolado do ator principal, ele será executado na thread principal. Caso contrário, não será executado na thread principal. Isso não se aplica apenas ao ator principal, mas a qualquer isolamento de ator em geral.Esta parte não tem nenhuma relação com a simultaneidade do Swift.
Timer.scheduledTimer
Executa um temporizador no loop de execução atual. Como isso é chamado na thread principal, o temporizador será executado no loop de execução associado à thread principal, que obviamente será executado na thread principal.Se você usar
@MainActor
a anotaçãoclass
,struct
,protocol
,enum
significa que esse tipo só pode ser usado a partir do MainActor (thread principal). E respondendo à sua pergunta: sim, qualquer coisa que você inserir nesse tipo será executada na thread principal ( https://developer.apple.com/videos/play/wwdc2021/10133 26:20)No seu exemplo, seu método
modifyRandomNumber
será executado no thread principal sempre sem exceções.Task
herda o isolamento do ator, o que significa que em sua classe todosTask
serão executados no thread principal.Se você não quiser herdar o isolamento do ator para sua tarefa e executar o código em um thread diferente, use
Task.detached
Ps Se você quiser quebrar o isolamento do ator para algum método, propriedade do seu tipo MainActor (ou algum ator), você pode anotar o método/propriedade como não isolado