He buscado en el libro Swift, pero no puedo encontrar la versión Swift de @synchronized. ¿Cómo hago exclusión mutua en Swift?
concurrency
mutex
swift
Cuenta
fuente
fuente
removeFirst()
?Respuestas:
Puedes usar GCD. Es un poco más detallado que
@synchronized
, pero funciona como un reemplazo:fuente
Estaba buscando esto yo mismo y llegué a la conclusión de que todavía no hay una construcción nativa dentro de Swift para esto.
Creé esta pequeña función auxiliar basada en algunos de los códigos que he visto de Matt Bridges y otros.
El uso es bastante sencillo
Hay un problema que he encontrado con esto. Pasar una matriz como el argumento de bloqueo parece causar un error de compilación muy obtuso en este punto. De lo contrario, parece funcionar como se desea.
fuente
@synchronized
bloque muy bien, pero tenga en cuenta que no es idéntica a una declaración verdadero bloque incorporado como el@synchronized
bloque en Objective-C, porquereturn
ybreak
declaraciones trabajo ya no saltar de la función / bucle que rodea al igual lo haría si fuera una declaración ordinaria.defer
palabra clave para garantizar queobjc_sync_exit
se llame incluso si seclosure
lanza.Me gusta y utilizo muchas de las respuestas aquí, así que elegiría la que mejor funcione para usted. Dicho esto, el método que prefiero cuando necesito algo como el objetivo-c
@synchronized
utiliza ladefer
declaración introducida en swift 2.Lo bueno de este método, es que su sección crítica puede salir del bloque de contención de cualquier manera deseada (por ejemplo,
return
,break
,continue
,throw
), y "las declaraciones dentro de la declaración de aplazamiento se ejecutan sin importar cómo se transfiere el control del programa." 1fuente
lock
? ¿Cómo selock
inicializa?lock
es cualquier objeto objetivo-c.Puede intercalar declaraciones entre
objc_sync_enter(obj: AnyObject?)
yobjc_sync_exit(obj: AnyObject?)
. La palabra clave @synchronized está utilizando esos métodos debajo de las cubiertas. es decirfuente
objc_sync_enter
yobjc_sync_exit
son métodos definidos en Objc-sync.h y son de código abierto: opensource.apple.com/source/objc4/objc4-371.2/runtime/…objc_sync_enter(…)
yobjc_sync_exit(…)
son encabezados públicos proporcionados por iOS / macOS / etc. API (parece que están dentro….sdk
de la rutausr/include/objc/objc-sync.h
) . La forma más fácil de averiguar si algo es una API pública o no es (en Xcode) escribir el nombre de la función (por ejemploobjc_sync_enter()
, no es necesario especificar argumentos para las funciones C) , luego intente hacer clic con el comando. Si le muestra el archivo de encabezado para esa API, entonces está bien (ya que no podría ver el encabezado si no fuera público) .El análogo de la
@synchronized
directiva de Objective-C puede tener un tipo de retorno arbitrario y un buenrethrows
comportamiento en Swift.El uso de la
defer
declaración permite devolver directamente un valor sin introducir una variable temporal.En Swift 2 agregue el
@noescape
atributo al cierre para permitir más optimizaciones:Basado en las respuestas de GNewc [1] (donde me gusta el tipo de retorno arbitrario) y Tod Cunningham [2] (donde me gusta
defer
).fuente
SWIFT 4
En Swift 4 puede usar las colas de despacho de GCD para bloquear recursos.
fuente
.serial
Parece no estar disponible. Pero.concurrent
está disponible. : /myObject.state = myObject.state + 1
simultáneamente, no contaría las operaciones totales, sino que arrojaría un valor no determinista. Para resolver ese problema, el código de llamada debe estar envuelto en una cola en serie para que tanto la lectura como la escritura ocurran atómicamente. Por supuesto, Obj-c@synchronised
tiene el mismo problema, por lo que su implementación es correcta.myObject.state += 1
es una combinación de una operación de lectura y luego de escritura. Algún otro hilo aún puede interponerse para establecer / escribir un valor. Según objc.io/blog/2018/12/18/atomic-variables , sería más fácil ejecutarloset
en un bloque / cierre de sincronización y no bajo la variable misma.Utilizando la respuesta de Bryan McLemore, la extendí para apoyar objetos que arrojan una mansión segura con la habilidad de aplazamiento Swift 2.0.
fuente
rethrows
para simplificar el uso con cierres sin tirar (no es necesario usartry
), como se muestra en mi respuesta .Para agregar la funcionalidad de retorno, puede hacer esto:
Posteriormente, puede llamarlo usando:
fuente
Swift 3
Este código tiene la capacidad de reingreso y puede funcionar con llamadas de función asincrónicas. En este código, después de llamar a someAsyncFunc (), se procesará otro cierre de función en la cola en serie, pero semaphore.wait () bloqueará hasta que se llame a signal (). internalQueue.sync no debe usarse, ya que bloqueará el hilo principal si no me equivoco.
objc_sync_enter / objc_sync_exit no es una buena idea sin manejo de errores.
fuente
En la sesión 414 "Comprender los bloqueos y los registros de bloqueos" de la WWDC 2018, muestran la siguiente manera utilizando DispatchQueues con sincronización.
En swift 4 debería ser algo como lo siguiente:
De todos modos, también puede hacer lecturas más rápidas utilizando colas concurrentes con barreras. Las lecturas de sincronización y asíncrona se realizan simultáneamente y la escritura de un nuevo valor espera a que finalicen las operaciones anteriores.
fuente
Use NSLock en Swift4:
fuente
En el moderno Swift 5, con capacidad de retorno:
Úselo así, para aprovechar la capacidad de valor de retorno:
O así de otra manera:
fuente
GCD
). Parece que esencialmente nadie usa o entiende cómo usarThread
. Estoy muy contento con eso, mientras queGCD
está lleno de problemas y limitaciones.Prueba: NSRecursiveLock
fuente
Figura Publicaré mi implementación de Swift 5, basada en las respuestas anteriores. ¡Gracias chicos! Me pareció útil tener uno que también devuelva un valor, así que tengo dos métodos.
Aquí hay una clase simple para hacer primero:
Luego úselo así si necesita un valor de retorno:
O:
fuente
public class func synced<T>(_ lock: Any, closure: () -> T)
, funciona para ambos, nulo y cualquier otro tipo. También está el material de rebrote.Detalles
xCode 8.3.1, swift 3.1
Tarea
Leer el valor de escritura de diferentes hilos (asíncrono).
Código
Uso
Muestra completa
fuente
Con los envoltorios de propiedades de Swift, esto es lo que estoy usando ahora:
Entonces puedes simplemente hacer:
o
Luego acceda a la variable como lo haría normalmente.
fuente
DispatchQueue
que está oculto para el usuario. Encontré esta referencia SO para tranquilizarme: stackoverflow.com/a/35022486/1060314En conclusión, aquí damos una forma más común que incluye el valor de retorno o nulo, y arrojamos
fuente
¿Por qué hacerlo difícil y molesto con las cerraduras? Utilice barreras de envío.
Una barrera de despacho crea un punto de sincronización dentro de una cola concurrente.
Mientras se está ejecutando, no se permite ejecutar ningún otro bloque en la cola, incluso si es concurrente y hay otros núcleos disponibles.
Si eso suena como un bloqueo exclusivo (escritura), lo es. Los bloques sin barrera pueden considerarse bloqueos compartidos (leídos).
Siempre que todo el acceso al recurso se realice a través de la cola, las barreras proporcionan una sincronización muy barata.
fuente
Basado en ɳeuroburɳ , pruebe un caso de subclase
Salida:
fuente
dispatch_barrier_async es la mejor manera, sin bloquear el hilo actual.
dispatch_barrier_async (accessQueue, {dictionary [object.ID] = object})
fuente
Otro método es crear una superclase y luego heredarla. De esta manera puedes usar GCD más directamente
fuente