2009-01-22 19 views

Respuesta

47

No está especificado por el estándar C; depende de la implementación de la biblioteca estándar C. De hecho, el estándar C ni siquiera menciona los hilos, ya que ciertos sistemas (por ejemplo, los sistemas integrados) no tienen subprocesamiento múltiple.

En la implementación de GNU (glibc), la mayoría de las funciones de nivel superior en stdio que se ocupan de los objetos FILE* son seguras para la ejecución de subprocesos. Los que generalmente no tienen unlocked en sus nombres (por ejemplo, getc_unlocked(3)). Sin embargo, la seguridad de subprocesos está en un nivel de llamada por función: si realiza múltiples llamadas al printf(3), por ejemplo, se garantiza que cada una de esas llamadas generará una salida atómica, pero otros subprocesos pueden imprimir cosas entre sus llamadas al printf(). Si desea asegurarse de que una secuencia de llamadas de E/S obtiene una salida atómica, puede rodearlas con un par de llamadas flockfile(3)/funlockfile(3) para bloquear el controlador FILE. Tenga en cuenta que estas funciones son reentrantes, por lo que puede llamar de manera segura al printf(), y eso no dará como resultado un punto muerto, aunque el propio printf() haga una llamada al flockfile().

El bajo nivel de E/S llama como write(2) debe ser flujos seguros, pero no estoy 100% seguro de eso - write() hace una llamada al sistema en el kernel para realizar E/S. Cómo exactamente sucede esto depende de qué kernel estás usando. Puede ser la instrucción sysenter o la instrucción int (interrupción) en sistemas anteriores. Una vez dentro del kernel, depende del kernel asegurarse de que la E/S sea segura para la ejecución de subprocesos. En una prueba que acabo de hacer con Darwin Kernel, la versión 8.11.1, write(2) parece ser segura para subprocesos.

+4

Multi-threading es común en embedded sistemas. – DanM

+22

Esta respuesta ignora que la pregunta fue etiquetada como unix/linux. POSIX requiere que stdio sea seguro para subprocesos, lo que es bastante desafortunado ya que mata el rendimiento y no hay forma práctica de operar en el mismo ARCHIVO desde múltiples subprocesos (los datos saldrán intercalados irremediablemente, la atomicidad estará solo en el nivel del personaje). –

+1

A veces está bastante bien si la salida está entrelazada, p. durante el registro a través de printf desde múltiples hilos. – nob

4

Es seguro para hilos; printf debe ser reentrante, y no causarás ninguna extrañeza o corrupción en tu programa.

No puede garantizar que su salida de un hilo no comience a la mitad de la salida de otro hilo. Si te preocupa, debes desarrollar tu propio código de salida bloqueado para evitar el acceso múltiple.

+0

Es probable que todas las llamadas a printf usen el mismo búfer para construir la cadena. Muchas implementaciones también comparten un búfer entre scanf y printf, lo que puede causar algunos errores que dependen de la depuración. –

+3

No sé lo que otros hacen, pero la biblioteca C de GNU es segura para subprocesos de manera predeterminada, por lo que no utilizará el mismo búfer. –

+1

No creo que 'printf' sea reentrante, vea http://stackoverflow.com/questions/3941271/why-are-malloc-and-printf-said-as-non-reentrant –

12

Ambos son seguros para subprocesos hasta el punto de que la aplicación no se bloqueará si hay varios hilos que los llamen en el mismo descriptor de archivo. Sin embargo, sin un cierto nivel de aplicación de bloqueo, lo que está escrito podría ser intercalado.

20

Si lo llama "hilo seguro" depende de su definición de hilo seguro. POSIX requiere stdio funciones para usar el bloqueo, por lo que su programa no se bloqueará, se dañarán los estados de los objetos FILE, etc. si usa printf simultáneamente desde múltiples hilos. Sin embargo, todas las operaciones stdio se especifican formalmente en términos de llamadas repetidas a fgetc y fputc, por lo que no se garantiza la atomicidad a mayor escala. Es decir, si los hilos 1 y 2 intentan imprimir "Hello\n" y "Goodbye\n" al mismo tiempo, no hay garantía de que la salida sea "Hello\nGoodbye\n" o "Goodbye\nHello\n". Podría ser "HGelolodboy\ne\n". En la práctica, la mayoría de las implementaciones adquirirán un único bloqueo para toda la llamada de escritura de nivel superior simplemente porque es más eficiente, pero su programa no debería asumirlo. Puede haber casos de esquina donde esto no se hace; por ejemplo, una implementación probablemente podría omitir por completo el bloqueo de flujos sin búfer.

Editar: El texto anterior sobre atomicidad es incorrecto. POSIX garantiza a todos los stdio operaciones son atómicas, pero la garantía se oculta en la documentación para flockfile: http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html

Todas las funciones que hacen referencia (FILE *) los objetos se comportan como si utilizan flockfile() y funlockfile() internamente para obtener la propiedad de estos objetos (ARCHIVO *).

Puede utilizar los flockfile, ftrylockfile y funlockfile funciones de sí mismo para lograr escrituras atómicas más grande que la sola función de guardia.

3

C obtuvo un nuevo estándar ya que se hizo esta pregunta (y la última respuesta).

C11 ahora viene con soporte multihilo y direcciones comportamiento de los flujos de multiproceso:

§7.21.2 corrientes

¶7 Cada corriente tiene un bloqueo asociado que se utiliza para evite carreras de datos cuando varios subprocesos de ejecución acceden a una secuencia y restringen el entrelazado de operaciones de flujo realizadas por varios hilos. Solo un hilo puede mantener este bloqueo a la vez. El bloqueo es reentrante: un solo hilo puede mantener el bloqueo varias veces en un momento dado.

¶8 Todas las funciones que leen, escriben, posicionan o consultan la posición de una secuencia bloquean la transmisión antes de acceder a ella. Liberan el bloqueo asociado con la transmisión cuando se completa el acceso.

Por lo tanto, una implementación con subprocesos C11 debe garantizar que el uso de printf es seguro para subprocesos.

está garantizada

Si atomicidad (como en ninguna intercalación 1), no fue tan claro para mí a primera vista, ya que la norma habla de restringir intercalado, en contraposición a la prevención de , que se encomendó para carreras de datos.

Me inclino por garantizarlo. El estándar habla de que restringe el entrelazado, ya que aún se permite que ocurra algún entrelazado que no cambie el resultado; p.fwrite algunos bytes, fseek atrás algunos más y fwrite hasta el desplazamiento original, de modo que ambos fwrite s están espalda con espalda. La implementación es libre de reordenar estos 2 fwrite sy combinarlos en una sola escritura.


1: Véase el texto tachado en R..'s answer para un ejemplo.