Pesquisei o livro Swift, mas não consigo encontrar a versão Swift de @synchronized. Como faço para exclusão mútua no Swift?
concurrency
mutex
swift
Conta
fonte
fonte
removeFirst()
?Respostas:
Você pode usar o GCD. É um pouco mais detalhado do que
@synchronized
, mas funciona como um substituto:fonte
Eu mesmo estava procurando isso e cheguei à conclusão de que ainda não há nenhuma construção nativa dentro do swift.
Eu criei essa pequena função auxiliar com base em alguns dos códigos que eu vi de Matt Bridges e outros.
O uso é bastante direto
Há um problema que encontrei com isso. Passar em uma matriz como o argumento de bloqueio parece causar um erro muito obtuso do compilador neste momento. Caso contrário, embora pareça funcionar como desejado.
fonte
@synchronized
bloco, mas observe que ele não é idêntico a uma instrução de bloco embutida real como o@synchronized
bloco no Objective-C, porque as instruçõesreturn
ebreak
não funcionam mais para pular da função / loop circundante como seria se fosse uma afirmação comum.defer
palavra-chave para garantir queobjc_sync_exit
seja chamado mesmo que sejaclosure
lançado.Gosto e uso muitas das respostas aqui, portanto, escolho o que for melhor para você. Dito isso, o método que eu prefiro quando preciso de algo como o objetivo-c
@synchronized
usa adefer
instrução introduzida no swift 2.A coisa agradável sobre este método, é que a sua seção crítica pode sair do bloco contendo de qualquer forma desejada (por exemplo,
return
,break
,continue
,throw
), e "as declarações dentro a declaração de adiamento são executados não importa como o controle do programa é transferido." 1fonte
lock
? Como élock
inicializado?lock
é qualquer objeto objetivo-c.Você pode colocar declarações entre
objc_sync_enter(obj: AnyObject?)
eobjc_sync_exit(obj: AnyObject?)
. A palavra-chave @synchronized está usando esses métodos nos bastidores. iefonte
objc_sync_enter
eobjc_sync_exit
os métodos são definidos no Objc-sync.h e são de código aberto: opensource.apple.com/source/objc4/objc4-371.2/runtime/…objc_sync_enter(…)
eobjc_sync_exit(…)
são cabeçalhos públicos fornecidos pelo iOS / macOS / etc. APIs (parece que elas estão dentro….sdk
do caminhousr/include/objc/objc-sync.h
) . A maneira mais fácil de descobrir se algo é uma API pública ou não é (no Xcode) digitar o nome da função (por exemploobjc_sync_enter()
, os argumentos não precisam ser especificados para as funções C) e , em seguida, clique com o botão direito do mouse. Se ele mostrar o arquivo de cabeçalho para essa API, você será bom (já que não conseguiria ver o cabeçalho se não fosse público) .O análogo da
@synchronized
diretiva do Objective-C pode ter um tipo de retorno arbitrário e um bomrethrows
comportamento no Swift.O uso da
defer
instrução permite retornar diretamente um valor sem introduzir uma variável temporária.No Swift 2, adicione o
@noescape
atributo ao fechamento para permitir mais otimizações:Com base nas respostas de GNewc [1] (onde eu gosto do tipo de retorno arbitrário) e Tod Cunningham [2] (onde eu gosto
defer
).fonte
SWIFT 4
No Swift 4, você pode usar as filas de despacho GCDs para bloquear recursos.
fonte
.serial
parece estar indisponível. Mas.concurrent
está disponível. : /myObject.state = myObject.state + 1
simultaneamente, não contaria o total de operações, mas produziria um valor não determinístico. Para resolver esse problema, o código de chamada deve ser agrupado em uma fila serial para que a leitura e a gravação ocorram atomicamente. É claro que os Obj-c@synchronised
têm o mesmo problema, portanto, nesse sentido, sua implementação está correta.myObject.state += 1
é uma combinação de uma operação de leitura e gravação. Outro segmento ainda pode estar entre eles para definir / escrever um valor. Conforme objc.io/blog/2018/12/18/atomic-variables , seria mais fácil executar oset
bloqueio / fechamento em sincronização e não a variável em si.Usando a resposta de Bryan McLemore, eu a estendi para apoiar objetos que oferecem uma mansão segura com a capacidade de adiamento do Swift 2.0.
fonte
rethrows
para simplificar o uso com fechamentos sem arremesso (sem necessidade de usartry
), conforme mostrado na minha resposta .Para adicionar funcionalidade de retorno, você pode fazer o seguinte:
Posteriormente, você pode chamá-lo usando:
fonte
Swift 3
Este código tem a capacidade de reentrada e pode funcionar com chamadas de função assíncronas. Nesse código, depois que someAsyncFunc () é chamado, outro fechamento de função na fila serial será processado, mas será bloqueado pelo semáforo.wait () até o sinal () ser chamado. internalQueue.sync não deve ser usado, pois ele bloqueará o thread principal se não me engano.
objc_sync_enter / objc_sync_exit não é uma boa ideia sem tratamento de erros.
fonte
Na sessão "Compreendendo falhas e logs de falhas" 414 da WWDC 2018, eles mostram a seguinte maneira, usando DispatchQueues com sincronização.
No swift 4 deve ser algo como o seguinte:
De qualquer forma, você também pode fazer leituras mais rapidamente usando filas simultâneas com barreiras. As leituras de sincronização e assíncrona são executadas simultaneamente e a gravação de um novo valor aguarda o término das operações anteriores.
fonte
Use o NSLock no Swift4:
fonte
No Swift 5 moderno, com capacidade de retorno:
Use-o assim, para aproveitar o recurso de valor de retorno:
Ou assim:
fonte
GCD
). Parece que essencialmente ninguém usa ou entende como usarThread
. Estou muito feliz com isso - ao passo queGCD
está cheio de truques e limitações.Experimente: NSRecursiveLock
fonte
Figura Vou postar minha implementação do Swift 5, construída com base nas respostas anteriores. Obrigado rapazes! Achei útil ter um que retorne um valor também, então eu tenho dois métodos.
Aqui está uma classe simples para fazer primeiro:
Em seguida, use-o assim se precisar de um valor de retorno:
Ou:
fonte
public class func synced<T>(_ lock: Any, closure: () -> T)
, funciona para ambos, nulo e qualquer outro tipo. Há também o material regrows.Detalhes
xCode 8.3.1, swift 3.1
Tarefa
Leia o valor de gravação de diferentes segmentos (assíncrono).
Código
Uso
Amostra Completa
fonte
Com os wrappers de propriedades do Swift, é isso que estou usando agora:
Então você pode simplesmente fazer:
ou
Em seguida, acesse a variável como faria normalmente.
fonte
DispatchQueue
que está oculto para o usuário. Encontrei esta referência SO para tranqüilizar: stackoverflow.com/a/35022486/1060314Em conclusão, aqui é apresentada uma maneira mais comum que inclui valor de retorno ou nulo e
fonte
Por que dificultar e aborrecer os bloqueios? Use Barreiras de Despacho.
Uma barreira de despacho cria um ponto de sincronização dentro de uma fila simultânea.
Enquanto estiver em execução, nenhum outro bloco na fila poderá executar, mesmo que seja concorrente e outros núcleos estejam disponíveis.
Se isso soa como um bloqueio exclusivo (gravação), é. Blocos sem barreira podem ser considerados bloqueios compartilhados (lidos).
Desde que todo o acesso ao recurso seja realizado através da fila, as barreiras fornecem uma sincronização muito barata.
fonte
Com base no obeuroburɳ , teste um caso de subclasse
Resultado:
fonte
dispatch_barrier_async é a melhor maneira, sem bloquear o segmento atual.
dispatch_barrier_async (accessQueue, {dictionary [object.ID] = object})
fonte
Outro método é criar uma superclasse e depois herdá-la. Dessa forma, você pode usar o GCD mais diretamente
fonte