¿Cuál es el significado del término "seguro para subprocesos"?

Respuestas:

256

De Wikipedia:

La seguridad de subprocesos es un concepto de programación informática aplicable en el contexto de programas de subprocesos múltiples. Un código es seguro para subprocesos si funciona correctamente durante la ejecución simultánea de múltiples subprocesos. En particular, debe satisfacer la necesidad de que varios subprocesos accedan a los mismos datos compartidos, y la necesidad de que un solo subproceso acceda a un dato compartido en un momento dado.

Hay algunas formas de lograr la seguridad del hilo:

Re-fascinación:

Escribir código de tal manera que pueda ser ejecutado parcialmente por una tarea, reingresado por otra tarea y luego reanudado desde la tarea original. Esto requiere guardar la información de estado en variables locales para cada tarea, generalmente en su pila, en lugar de en variables estáticas o globales.

Exclusión mutua:

El acceso a los datos compartidos se serializa utilizando mecanismos que aseguran que solo un hilo lea o escriba los datos compartidos en cualquier momento. Se requiere un gran cuidado si un fragmento de código accede a múltiples datos compartidos; los problemas incluyen condiciones de carrera, puntos muertos, bloqueos en vivo, inanición y varios otros males enumerados en muchos libros de texto de sistemas operativos.

Almacenamiento local de subprocesos:

Las variables se localizan para que cada hilo tenga su propia copia privada. Estas variables conservan sus valores a través de la subrutina y otros límites de código, y son seguros para subprocesos ya que son locales para cada subproceso, aunque el código que accede a ellos puede ser reentrante.

Operaciones atómicas:

Se accede a los datos compartidos mediante operaciones atómicas que no pueden ser interrumpidas por otros hilos. Esto generalmente requiere el uso de instrucciones especiales de lenguaje de máquina, que pueden estar disponibles en una biblioteca de tiempo de ejecución. Como las operaciones son atómicas, los datos compartidos siempre se mantienen en un estado válido, sin importar a qué otros hilos accedan. Las operaciones atómicas forman la base de muchos mecanismos de bloqueo de hilos.

Lee mas:

http://en.wikipedia.org/wiki/Thread_safety


Blauohr
fuente
44
A este enlace, técnicamente, le faltan varios puntos críticos. La memoria compartida en cuestión debe ser mutable (la memoria de solo lectura no puede ser insegura de subprocesos), y los múltiples subprocesos deben, a) realizar múltiples operaciones de escritura en la memoria durante la mitad de la cual la memoria es inconsistente (incorrecta) estado, y b) permitir que otros hilos interrumpan ese hilo mientras la memoria es inconsistente.
Charles Bretana
20
cuando se busca el primer resultado de Google es wiki, no tiene sentido hacer que sea redundante aquí.
Ranvir
¿Qué quiere decir "un código que accede a una función"? La función que se ejecuta en sí misma es el código, ¿no?
Koray Tugay
82

El código seguro para subprocesos es un código que funcionará incluso si muchos subprocesos lo ejecutan simultáneamente.

http://mindprod.com/jgloss/threadsafe.html

Marek Blotny
fuente
34
Dentro del mismo proceso!
Ali Afshar el
De hecho, en el mismo proceso :)
Marek Blotny
44
"Escribir código que se ejecutará de manera estable durante semanas requiere una paranoia extrema". Esa es una cita que me gusta :)
Jim T
55
duh! ¡Esta respuesta simplemente reafirma la pregunta! --- ¿Y por qué solo dentro del mismo proceso? Si el código falla cuando varios subprocesos lo ejecutan desde diferentes procesos, entonces, posiblemente, (la "memoria compartida" podría estar en un archivo de disco), ¡NO es seguro para subprocesos!
Charles Bretana
1
@ mg30rg. Quizás la confusión es el resultado de pensar de alguna manera que cuando un bloque de código está siendo ejecutado por múltiples procesos, pero solo por un hilo por proceso, eso, de alguna manera, sigue siendo un escenario de "Un solo hilo", no un escenario de múltiples hilos . Esta idea ni siquiera está mal. Es solo una mala definición. Claramente, los procesos múltiples generalmente no se ejecutan en el mismo hilo de manera sincronizada (excepto en escenarios raros donde los procesos por diseño se coordinan entre sí y el sistema operativo comparte hilos entre procesos).
Charles Bretana
50

Una pregunta más informativa es qué hace que el código no sea seguro para subprocesos, y la respuesta es que hay cuatro condiciones que deben ser ciertas ... Imagine el siguiente código (y es la traducción del lenguaje automático)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. La primera condición es que hay ubicaciones de memoria a las que se puede acceder desde más de un subproceso. Por lo general, estas ubicaciones son variables globales / estáticas o se puede acceder a la memoria del montón desde variables globales / estáticas. Cada subproceso tiene su propio marco de pila para las variables locales con ámbito de función / método, por lo que estas variables de función / método local, otoh, (que están en la pila) solo son accesibles desde el único subproceso que posee esa pila.
  2. La segunda condición es que hay una propiedad (a menudo llamada invariante ), que está asociada con estas ubicaciones de memoria compartida, que debe ser verdadera o válida, para que el programa funcione correctamente. En el ejemplo anterior, la propiedad es que " totalRequests debe representar con precisión el número total de veces que un subproceso ha ejecutado cualquier parte de la declaración de incremento ". Por lo general, esta propiedad invariable debe ser verdadera (en este caso, totalRequests debe contener un recuento preciso) antes de que ocurra una actualización para que la actualización sea correcta.
  3. La tercera condición es que la propiedad invariante NO se mantiene durante alguna parte de la actualización real. (Es transitoriamente inválido o falso durante alguna parte del procesamiento). En este caso particular, desde el momento en que se obtiene totalRequests hasta el momento en que se almacena el valor actualizado, totalRequests no satisface la invariante.
  4. La cuarta y última condición que debe ocurrir para que una carrera suceda (y para que el código NO sea ​​"seguro para subprocesos") es que otro subproceso debe poder acceder a la memoria compartida mientras el invariante está roto, causando inconsistencias o comportamiento incorrecto
Charles Bretana
fuente
66
Esto cubre solo lo que se conoce como carreras de datos y, por supuesto, es importante. Sin embargo, hay otras formas en que el código no puede ser seguro para subprocesos, por ejemplo, un bloqueo incorrecto que puede conducir a puntos muertos. Incluso algo simple como llamar a System.exit () en algún lugar de un subproceso de Java hace que ese código no sea seguro para subprocesos.
Ingo
2
Supongo que hasta cierto punto esto es semántica, pero diría que un código de bloqueo incorrecto que puede causar un punto muerto no hace que el código sea inseguro. Primero, no hay necesidad de bloquear el código en primer lugar a menos que sea posible una condición de carrera, como se describió anteriormente. Luego, si escribe el código de bloqueo de tal manera que cause un punto muerto, eso no es inseguro, es solo un código incorrecto.
Charles Bretana
1
Pero tenga en cuenta que el punto muerto no ocurrirá cuando se ejecute un solo subproceso, por lo que para la mayoría de nosotros esto seguramente caería bajo el significado intuitivo de (no) "seguro para subprocesos".
Jon Coombs
Bueno, los puntos muertos no pueden ocurrir a menos que esté ejecutando múltiples subprocesos, por supuesto, pero eso es como decir que los problemas de red no pueden ocurrir si está ejecutando en una máquina. También pueden ocurrir otros problemas de un solo subproceso, si el programador escribe el código para que salga de las líneas críticas de código antes de completar la actualización, y modifique la variable en alguna otra subrutina.
Charles Bretana
34

Me gusta la definición de la concurrencia de Java de Brian Goetz en la práctica por su amplitud

"Una clase es segura para subprocesos si se comporta correctamente cuando se accede desde múltiples subprocesos, independientemente de la programación o intercalación de la ejecución de esos subprocesos por el entorno de tiempo de ejecución, y sin sincronización adicional u otra coordinación por parte del código de llamada. "

Buu Nguyen
fuente
Esta definición es incompleta y no específica, y definitivamente no es exhaustiva. ¿Cuántas veces debe funcionar de forma segura, solo una vez? ¿diez veces? ¿cada vez? 80% del tiempo? y no especifica qué lo hace "inseguro". Si no puede ejecutarse de manera segura, pero la falla se debe a que hay un error de división por cero, ¿eso hace que el hilo sea "inseguro"?
Charles Bretana
Sea más civilizado la próxima vez y tal vez podamos discutir. Esto no es Reddit y no estoy de humor para hablar con gente grosera.
Buu Nguyen
Sus comentarios interpretativos sobre la definición de otra persona como insultos para usted mismo son reveladores. Necesita leer y comprender la sustancia antes de reaccionar emocionalmente. Nada incivilizado sobre mi comentario. Estaba haciendo un punto sobre el significado de la definición. Lo siento si los ejemplos que utilicé para ilustrar el punto te hicieron sentir incómodo.
Charles Bretana
28

Como otros han señalado, la seguridad del subproceso significa que un código funcionará sin errores si es utilizado por más de un subproceso a la vez.

Vale la pena tener en cuenta que esto a veces tiene un costo, tiempo de computadora y codificación más compleja, por lo que no siempre es deseable. Si una clase se puede usar de forma segura en un solo subproceso, puede ser mejor hacerlo.

Por ejemplo, Java tiene dos clases que son casi equivalentes, StringBuffery StringBuilder. La diferencia es que StringBufferes seguro para subprocesos, por lo que una sola instancia de a StringBufferpuede ser utilizada por múltiples subprocesos a la vez. StringBuilderno es seguro para subprocesos y está diseñado como un reemplazo de mayor rendimiento para esos casos (la gran mayoría) cuando el String está construido por un solo subproceso.

Marcus Downing
fuente
22

El código seguro para subprocesos funciona según lo especificado, incluso cuando se ingresan simultáneamente por diferentes subprocesos. Esto a menudo significa que las estructuras de datos internas o las operaciones que deberían ejecutarse sin interrupciones están protegidas contra diferentes modificaciones al mismo tiempo.

Mnementh
fuente
21

Una forma más fácil de entenderlo es lo que hace que el código no sea seguro para subprocesos. Hay dos problemas principales que harán que una aplicación enhebrada tenga un comportamiento no deseado.

  • Acceso a la variable compartida sin bloqueo
    Esta variable podría ser modificada por otro hilo mientras se ejecuta la función. Desea evitarlo con un mecanismo de bloqueo para asegurarse del comportamiento de su función. La regla general es mantener la cerradura por el menor tiempo posible.

  • Punto muerto causado por la dependencia mutua de la variable compartida
    Si tiene dos variables compartidas A y B. En una función, bloquea A primero y luego bloquea B. En otra función, comienza a bloquear B y después de un tiempo, bloquea A. es un punto muerto potencial donde la primera función esperará a que B se desbloquee cuando la segunda función esperará a que A se desbloquee. Este problema probablemente no ocurrirá en su entorno de desarrollo y solo de vez en cuando. Para evitarlo, todas las cerraduras deben estar siempre en el mismo orden.

Hapkido
fuente
9

Si y no.

La seguridad de subprocesos es un poco más que solo asegurarse de que solo un subproceso a la vez acceda a sus datos compartidos. Debe garantizar el acceso secuencial a los datos compartidos y, al mismo tiempo, evitar las condiciones de carrera , los puntos muertos , los bloqueos en vivo y la falta de recursos .

Los resultados impredecibles cuando se ejecutan varios subprocesos no es una condición necesaria del código seguro para subprocesos, pero a menudo es un subproducto. Por ejemplo, podría tener un esquema de productor-consumidor configurado con una cola compartida, un subproceso de productor y pocos subprocesos de consumidor, y el flujo de datos podría ser perfectamente predecible. Si comienza a presentar más consumidores, verá resultados más aleatorios.

Bill el lagarto
fuente
9

En esencia, muchas cosas pueden salir mal en un entorno de subprocesos múltiples (reordenamiento de instrucciones, objetos parcialmente construidos, la misma variable que tiene diferentes valores en diferentes subprocesos debido al almacenamiento en caché a nivel de CPU, etc.).

Me gusta la definición dada por Java Concurrency in Practice :

Una [porción de código] es segura para subprocesos si se comporta correctamente cuando se accede desde múltiples subprocesos, independientemente de la programación o intercalación de la ejecución de esos subprocesos por el entorno de tiempo de ejecución, y sin sincronización adicional u otra coordinación por parte del código de llamada

Por correctamente , significan que el programa se comporta de conformidad con sus especificaciones.

Ejemplo contribuido

Imagine que implementa un contador. Se podría decir que se comporta correctamente si:

  • counter.next() nunca devuelve un valor que ya se ha devuelto antes (suponemos que no hay desbordamiento, etc. por simplicidad)
  • todos los valores del 0 al valor actual se han devuelto en algún momento (no se omite ningún valor)

Un contador seguro de subprocesos se comportaría de acuerdo con esas reglas independientemente de cuántos subprocesos accedan simultáneamente (lo que normalmente no sería el caso de una implementación ingenua).

Nota: publicación cruzada en programadores

asilias
fuente
8

Simplemente, el código funcionará bien si muchos hilos están ejecutando este código al mismo tiempo.

Greg Balajewicz
fuente
5

No confunda la seguridad del hilo con el determinismo. El código seguro para subprocesos también puede ser no determinista. Dada la dificultad de los problemas de depuración con código roscado, este es probablemente el caso normal. :-)

La seguridad de subprocesos simplemente garantiza que cuando un subproceso está modificando o leyendo datos compartidos, ningún otro subproceso puede acceder a él de una manera que cambie los datos. Si su código depende de un cierto orden de ejecución para su corrección, entonces necesita otros mecanismos de sincronización más allá de los necesarios para la seguridad del subproceso para garantizar esto.

tvanfosson
fuente
5

Me gustaría agregar más información sobre otras buenas respuestas.

La seguridad de subprocesos implica que varios subprocesos pueden escribir / leer datos en el mismo objeto sin errores de inconsistencia de memoria. En un programa altamente multiproceso, un programa seguro para subprocesos no causa efectos secundarios a los datos compartidos .

Eche un vistazo a esta pregunta SE para obtener más detalles:

¿Qué significa threadsafe?

El programa seguro para subprocesos garantiza la coherencia de la memoria .

Desde la página de documentación de Oracle en API concurrente avanzada:

Propiedades de consistencia de memoria:

El Capítulo 17 de la Especificación del lenguaje Java ™ define la relación que ocurre antes de las operaciones de memoria, tales como lecturas y escrituras de variables compartidas. Se garantiza que los resultados de una escritura de un hilo sean visibles para una lectura de otro hilo solo si la operación de escritura ocurre antes de la operación de lectura .

Las construcciones synchronizedy volatile, así como los métodos Thread.start()y Thread.join(), pueden formar relaciones que suceden antes .

Los métodos de todas las clases java.util.concurrenty sus subpaquetes amplían estas garantías a la sincronización de nivel superior. En particular:

  1. Las acciones en un hilo antes de colocar un objeto en cualquier colección concurrente suceden antes de las acciones posteriores al acceso o eliminación de ese elemento de la colección en otro hilo.
  2. Las acciones en un subproceso antes de la presentación de un Runnablea Executorsuceder, antes de que comience su ejecución. Del mismo modo para Callables sometidos a un ExecutorService.
  3. Acciones tomadas por el cálculo asincrónico representado por Futureacciones anteriores a la recuperación posterior del resultado a través Future.get()de otro hilo.
  4. Acciones previas a "liberar" métodos de sincronizador , como Lock.unlock, Semaphore.release, and CountDownLatch.countDownacciones de suceder antes de un método exitoso de "adquisición", como Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.awaiten el mismo objeto sincronizador en otro subproceso.
  5. Para cada par de subprocesos que intercambian objetos con éxito a través de un Exchanger, las acciones previas a exchange()cada subproceso suceden, antes de las posteriores al intercambio correspondiente () en otro subproceso.
  6. Las acciones previas a la llamada CyclicBarrier.awaity Phaser.awaitAdvance(así como sus variantes) suceden antes de las acciones realizadas por la acción de barrera, y las acciones realizadas por la acción de barrera suceden antes de las acciones posteriores a un retorno exitoso de la espera correspondiente en otros hilos.
Ravindra babu
fuente
4

Para completar otras respuestas:

La sincronización es solo una preocupación cuando el código en su método hace una de dos cosas:

  1. funciona con algún recurso externo que no es seguro para subprocesos.
  2. Lee o cambia un objeto persistente o un campo de clase

Esto significa que las variables definidas DENTRO de su método son siempre seguras. Cada llamada a un método tiene su propia versión de estas variables. Si el método es llamado por otro hilo, o por el mismo hilo, o incluso si el método se llama a sí mismo (recursión), los valores de estas variables no se comparten.

No se garantiza que la programación de subprocesos sea redonda . Una tarea puede acaparar totalmente la CPU a expensas de hilos de la misma prioridad. Puedes usar Thread.yield () para tener conciencia. Puede usar (en java) Thread.setPriority (Thread.NORM_PRIORITY-1) para reducir la prioridad de un hilo

Además ten cuidado con:

  • el gran costo de tiempo de ejecución (ya mencionado por otros) en aplicaciones que iteran sobre estas estructuras "seguras para subprocesos".
  • Se supone que Thread.sleep (5000) duerme durante 5 segundos. Sin embargo, si alguien cambia la hora del sistema, puede dormir durante mucho tiempo o no tener tiempo. El sistema operativo registra el tiempo de activación en forma absoluta, no relativa.
VonC
fuente
2

Si y si. Implica que los datos no son modificados por más de un hilo simultáneamente. Sin embargo, su programa podría funcionar como se esperaba y parecer seguro para subprocesos, incluso si fundamentalmente no lo es.

Tenga en cuenta que la imprevisibilidad de los resultados es una consecuencia de las "condiciones de carrera" que probablemente provoquen que los datos se modifiquen en un orden diferente al esperado.

Steve Knight
fuente
2

Respondamos esto con el ejemplo:

class NonThreadSafe {

    private int counter = 0;

    public boolean countTo10() {
        count = count + 1;
        return (count == 10);
    }

El countTo10método agrega uno al contador y luego devuelve verdadero si el recuento ha alcanzado 10. Solo debe devolver verdadero una vez.

Esto funcionará siempre que solo un hilo ejecute el código. Si dos hilos ejecutan el código al mismo tiempo, pueden ocurrir varios problemas.

Por ejemplo, si el recuento comienza como 9, un subproceso podría agregar 1 al recuento (haciendo 10) pero luego un segundo subproceso podría ingresar al método y agregar nuevamente 1 (haciendo 11) antes de que el primer subproceso tenga la oportunidad de ejecutar la comparación con 10 Luego, ambos hilos hacen la comparación y descubren que el conteo es 11 y ninguno devuelve verdadero.

Entonces este código no es seguro para subprocesos.

En esencia, todos los problemas de subprocesos múltiples son causados ​​por alguna variación de este tipo de problema.

La solución es garantizar que la suma y la comparación no puedan separarse (por ejemplo, rodeando las dos declaraciones con algún tipo de código de sincronización) o ideando una solución que no requiera dos operaciones. Tal código sería seguro para subprocesos.

rghome
fuente
1

Al menos en C ++, pienso en thread-safe como un nombre poco apropiado ya que deja mucho fuera del nombre. Para ser seguro para subprocesos, el código generalmente debe ser proactivo al respecto. Generalmente no es una cualidad pasiva.

Para que una clase sea segura, tiene que tener características "adicionales" que agreguen sobrecarga. Estas características son parte de la implementación de la clase y, en general, ocultas de la interfaz. Es decir, diferentes subprocesos pueden acceder a cualquiera de los miembros de la clase sin tener que preocuparse de entrar en conflicto con un acceso concurrente por un subproceso diferente Y pueden hacerlo de una manera muy perezosa, usando el estilo de codificación humana normal simple y antiguo, sin tener que hacerlo todas esas cosas locas de sincronización que ya están incluidas en las entrañas del código que se llama.

Y es por eso que algunas personas prefieren usar el término sincronizado internamente .

Conjuntos de terminología

Hay tres conjuntos principales de terminología para estas ideas que he encontrado. El primero e históricamente más popular (pero peor) es:

  1. a salvo de amenazas
  2. no es seguro para hilos

El segundo (y mejor) es:

  1. prueba de hilo
  2. hilo compatible
  3. hilo hostil

Un tercero es:

  1. sincronizado internamente
  2. sincronizado externamente
  3. no sincronizable

Analogías

hilo seguro ~ a prueba de hilo ~ sincronizado internamente

Un ejemplo de un internamente sincronizado (aka. Flujos seguros o de prueba del hilo del sistema) es un restaurante donde una gran cantidad te recibe en la puerta, y Deshabilita cola de sí mismo. El anfitrión es parte del mecanismo del restaurante para tratar con múltiples clientes, y puede usar algunos trucos bastante complicados para optimizar el asiento de los clientes que esperan, como tener en cuenta el tamaño de su grupo o cuánto tiempo parecen tener. , o incluso hacer reservas por teléfono. El restaurante está sincronizado internamente porque todo esto es parte de la interfaz para interactuar con él.

no es seguro para subprocesos (pero agradable) ~ compatible con subprocesos ~ sincronizado externamente ~ sin subprocesos

Supongamos que vas al banco. Hay una línea, es decir, contención para los cajeros bancarios. Como no eres un salvaje, reconoces que lo mejor que puedes hacer en medio de una disputa por un recurso es hacer cola como un ser civilizado. Nadie técnicamente te obliga a hacer esto. Esperamos que tenga la programación social necesaria para hacerlo por su cuenta. En este sentido, el lobby del banco está sincronizado externamente. ¿Deberíamos decir que no es seguro? eso es lo que la implicación es que si vas con el hilo de seguridad , hilo insegura conjunto terminología bipolar. No es un muy buen conjunto de términos. La mejor terminología está sincronizada externamente,El lobby del banco no es hostil para que varios clientes accedan a él, pero tampoco hace el trabajo de sincronizarlos. Los clientes lo hacen ellos mismos.

Esto también se llama "rosca libre", donde "libre" es como "libre de piojos", o en este caso, cerraduras. Bueno, más exactamente, primitivas de sincronización. Eso no significa que el código pueda ejecutarse en múltiples hilos sin esas primitivas. Simplemente significa que no viene con ellos ya instalados y depende de usted, el usuario del código, instalarlos usted mismo como mejor le parezca. La instalación de sus propias primitivas de sincronización puede ser difícil y requiere pensar mucho en el código, pero también puede conducir al programa más rápido posible al permitirle personalizar cómo se ejecuta el programa en las CPU hipertrontadas de hoy.

no es seguro para subprocesos (y está mal) ~ subproceso hostil ~ no sincronizable

Un ejemplo de la analogía cotidiana de un sistema hostil es un tirón con un auto deportivo que se niega a usar sus luces intermitentes y cambiar de carril de manera involuntaria. Su estilo de conducción es hostil o no psicronizable porque no tiene forma de coordinarse con ellos, y esto puede conducir a una disputa por el mismo carril, sin resolución, y por lo tanto un accidente ya que dos autos intentan ocupar el mismo espacio, sin ningún protocolo para Prevenir esto. Este patrón también se puede considerar más ampliamente como antisocial, lo que prefiero porque es menos específico para los hilos y, por lo tanto, más generalmente aplicable a muchas áreas de programación.

¿Por qué hilo seguro et al. son un mal conjunto de terminología

El primer y más antiguo conjunto de terminología no logra hacer una distinción más fina entre la hostilidad del hilo y la compatibilidad del hilo . La compatibilidad de subprocesos es más pasiva que la denominada seguridad de subprocesos, pero eso no significa que el código llamado no sea seguro para el uso concurrente de subprocesos. Simplemente significa que es pasivo acerca de la sincronización que permitiría esto, posponiéndolo en el código de llamada, en lugar de proporcionarlo como parte de su implementación interna. La compatibilidad de subprocesos es la forma en que el código probablemente debería escribirse de manera predeterminada en la mayoría de los casos, pero lamentablemente a menudo también se lo considera erróneamente como inseguro, como si fuera intrínsecamente anti seguridad, lo que es un gran punto de confusión para los programadores.

NOTA: Muchos manuales de software en realidad usan el término "seguro para subprocesos" para referirse a "compatible con subprocesos", lo que agrega aún más confusión a lo que ya era un desastre. Evito el término "seguro para subprocesos" e "inseguro para subprocesos" a toda costa por esta misma razón, ya que algunas fuentes llamarán a algo "seguro para subprocesos" mientras que otros lo llamarán "inseguro para subprocesos" porque no pueden estar de acuerdo sobre si tiene que cumplir con algunos estándares adicionales de seguridad (primitivas de sincronización), o simplemente NO ser hostil para ser considerado "seguro". Así que evite esos términos y use los términos más inteligentes en su lugar, para evitar comunicaciones erróneas peligrosas con otros ingenieros.

Recordatorio de nuestros objetivos.

Esencialmente, nuestro objetivo es subvertir el caos.

Hacemos eso creando sistemas deterministas en los que podemos confiar. El determinismo es costoso, principalmente debido a los costos de oportunidad de perder paralelismo, canalización y reordenamiento. Tratamos de minimizar la cantidad de determinismo que necesitamos para mantener nuestros costos bajos, al tiempo que evitamos tomar decisiones que erosionen aún más el poco determinismo que podemos permitirnos.

La sincronización de hilos se trata de aumentar el orden y disminuir el caos. Los niveles en los que hace esto corresponden a los términos mencionados anteriormente. El nivel más alto significa que un sistema se comporta de una manera completamente predecible cada vez. El segundo nivel significa que el sistema se comporta lo suficientemente bien como para que el código de llamada pueda detectar de manera confiable la imprevisibilidad. Por ejemplo, una activación espuria en una variable de condición o una falla al bloquear un mutex porque no está listo. El tercer nivel significa que el sistema no se comporta lo suficientemente bien como para jugar con nadie más y solo se puede ejecutar con un solo hilo sin incurrir en caos.

Daniel Russell
fuente
1

En lugar de pensar que el código o las clases son seguros para los hilos o no, creo que es más útil pensar que las acciones son seguras para los hilos. Dos acciones son seguras para subprocesos si se comportan como se especifica cuando se ejecutan desde contextos de subprocesos arbitrarios. En muchos casos, las clases admitirán algunas combinaciones de acciones de manera segura para subprocesos y otras no.

Por ejemplo, muchas colecciones, como las listas de matrices y los conjuntos de hash, garantizarán que si inicialmente se accede a ellos exclusivamente con un hilo, y nunca se modifican después de que una referencia sea visible para cualquier otro hilo, cualquier combinación puede leerlos de manera arbitraria. de hilos sin interferencia.

Más interesante aún, algunas colecciones de hash-sets, como la original no genérica en .NET, pueden ofrecer una garantía de que siempre que no se elimine ningún elemento, y siempre que solo un hilo los escriba, cualquier hilo que intente leer la colección se comportará como si tuviera acceso a una colección donde las actualizaciones podrían retrasarse y ocurrir en un orden arbitrario, pero que de lo contrario se comportarían normalmente. Si el hilo # 1 agrega X y luego Y, y el hilo # 2 busca y ve Y y luego X, sería posible que el hilo # 2 vea que Y existe pero X no; si dicho comportamiento es "seguro para subprocesos" dependerá de si el subproceso # 2 está preparado para enfrentar esa posibilidad.

Como nota final, algunas clases, especialmente el bloqueo de bibliotecas de comunicaciones, pueden tener un método "cerrar" o "Eliminar" que es seguro para subprocesos con respecto a todos los demás métodos, pero ningún otro método que sea seguro para subprocesos con respecto a El uno al otro. Si un hilo realiza una solicitud de lectura de bloqueo y un usuario del programa hace clic en "cancelar", no habría forma de que el hilo que intenta realizar la lectura no emitiera una solicitud de cierre. Sin embargo, la solicitud de cierre / eliminación puede establecer asincrónicamente un indicador que hará que la solicitud de lectura se cancele lo antes posible. Una vez que se realiza el cierre en cualquier subproceso, el objeto se volvería inútil y todos los intentos de acciones futuras fallarían de inmediato,

Super gato
fuente
0

En palabras más simples: P Si es seguro ejecutar múltiples hilos en un bloque de código, es seguro para hilos *

*las condiciones se aplican

Las condiciones son mencionadas por otros respondedores como 1. El resultado debería ser el mismo si ejecuta un hilo o varios hilos sobre él, etc.

desgastado
fuente