"_thread" --- API de bajo nivel para manejo de hilos
****************************************************

======================================================================

Este módulo ofrece primitivas de bajo nivel para trabajar con
múltiples *threads* o hilos (también llamados *light-weight processes*
o *tasks*) -- múltiples hilos de control compartiendo su espacio de
datos global. Para sincronizar, provee "candados" simples (también
llamados  *mutexes* o *binary semaphores*). El módulo "threading"
provee una API de manejo de hilos más fácil de usar y de más alto
nivel, construida sobre este módulo.

Distinto en la versión 3.7: Este módulo solía ser opcional, pero ahora
está siempre disponible.

Este módulo define las siguientes constantes y funciones:

exception _thread.error

   Lanzado ante errores específicos de un hilo.

   Distinto en la versión 3.3: Ahora es un sinónimo de la excepción
   incorporada "RuntimeError".

_thread.LockType

   Este es el tipo de los objetos candado (*lock objects*).

_thread.start_new_thread(function, args[, kwargs])

   Inicia un nuevo hilo y retorna su identificador. El hilo ejecuta la
   función *function* con la lista de argumentos *args* (que debe ser
   una tupla). El argumento opcional *kwargs* especifica un
   diccionario de argumentos por palabras clave.

   Cuando la función retorna, el hilo finaliza silenciosamente.

   Cuando la función termina con una excepción no gestionada, se
   invoca a "sys.unraisablehook()" para que gestione la excepción. El
   atributo *object* del argumento gancho (*hook*), es *function*. Por
   defecto, se muestra un seguimiento de pila y luego el hilo sale
   (pero los otros hilos continúan funcionando).

   Cuando la función lanza una excepción "SystemExit", se ignora
   silenciosamente.

   Distinto en la versión 3.8: Ahora se utiliza "sys.unraisablehook()"
   para gestionar las excepciones no gestionadas.

_thread.interrupt_main(signum=signal.SIGINT, /)

   Simular el efecto de una señal que llega al hilo principal. Un hilo
   puede usar esta función para interrumpir el hilo principal, aunque
   no hay garantías de que la interrupción ocurrirá inmediatamente.

   If given, *signum* is the number of the signal to simulate. If
   *signum* is not given, "signal.SIGINT" is simulated.

   If the given signal isn't handled by Python (it was set to
   "signal.SIG_DFL" or "signal.SIG_IGN"), this function does nothing.

   Distinto en la versión 3.10: Se agrega el argumento *signum* para
   personalizar el número de señal.

   Nota:

     Esto no emite la señal correspondiente, sino que programa una
     llamada al controlador asociado (si existe). Si realmente se
     quiere emitir la señal, se utiliza "signal.raise_signal()".

_thread.exit()

   Lanza la excepción "SystemExit". Cuando no es gestionada, causa que
   el hilo salga silenciosamente.

_thread.allocate_lock()

   Retorna un nuevo objeto candado (*lock object*). Los métodos de los
   candados se describen más abajo. El candado está abierto al inicio.

_thread.get_ident()

   Retorna el 'identificador de hilo' (*thread identifier*) del hilo
   actual. Es un entero distinto de cero. Su valor no tiene un
   significado directo, tiene la intención de ser utilizada como una
   *cookie* mágica para, por ejemplo, indexar un diccionario con datos
   específicos del hilo. Los identificadores de hilo pueden reciclarse
   cuando un hilo sale y otro se crea.

_thread.get_native_id()

   Retorna el ID de hilo nativo integral del hilo asignado por el
   kernel. Es un entero no-negativo. Su valor puede utilizarse para
   identificar inequívocamente este hilo en particular en todo el
   sistema (hasta que el hilo termine, luego de lo cual el valor puede
   ser reciclado por el Sistema Operativo).

   Disponibilidad: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD,
   AIX.

   Nuevo en la versión 3.8.

_thread.stack_size([size])

   Retorna el tamaño de la pila del hilo (*thread stack*) utilizada al
   crear nuevos hilos. El argumento opcional *size* especifica el
   tamaño de la pila a utilizar en los hilos que se creen a
   continuación, y debe ser 0 (utiliza el valor por defecto de la
   plataforma o el configurado) o un entero positivo de al menos 32768
   (32KiB). Si *size* no se especifica, se utiliza 0. Si no está
   soportado el cambio del tamaño de pila del hilo, se lanza una
   excepción "RuntimeError". Si la pila especificada es inválida se
   lanza un "ValueError" y el tamaño de la pila no se modifica. 32KiB
   es actualmente el menor valor soportado para el tamaño de la pila,
   para garantizar suficiente espacio en la misma para que quepa el
   propio intérprete. Tenga en cuenta que alguna plataformas pueden
   tener restricciones particulares en los valores para el tamaño de
   la pila, como requerir un mínimo que supere los 32KiB, o requerir
   una asignación en múltiplos del tamaño de página de memoria del
   sistema. Es necesario consultar la documentación de la plataforma
   para mayor información (son habituales las páginas de 4KiB; usar
   múltiplos de 4096 para el tamaño de pila es la estrategia sugerida
   si no se cuenta con información más específica).

   Disponibilidad: Windows, hilos POSIX (también llamados  pthreads).

   Unix platforms with POSIX threads support.

_thread.TIMEOUT_MAX

   The maximum value allowed for the *timeout* parameter of
   "Lock.acquire". Specifying a timeout greater than this value will
   raise an "OverflowError".

   Nuevo en la versión 3.2.

Los objetos candado (*lock objects*) tienen los siguientes métodos:

lock.acquire(blocking=True, timeout=-1)

   Sin ningún argumento opcional, este método adquiere el candado
   incondicionalmente, si es necesario esperando que éste sea liberado
   por otro hilo (solamente un hilo por vez puede adquirir un candado;
   para eso existen).

   Si el argumento *blocking* está presente, la acción depende de su
   valor: si es False, el candado es adquirido sólo si puede ser
   adquirido inmediatamente sin espera, en cambio si es True, el
   candado es adquirido incondicionalmente como arriba.

   Si el argumento de punto flotante *timeout* está presente y es
   positivo, éste especifica el tiempo máximo de espera en segundos
   antes de retornar. Un argumento *timeout* negativo especifica una
   espera ilimitada. No se puede especificar un *timeout* si
   *blocking* es False.

   El valor de retorno es "True" si el candado (*lock*) se adquirió
   exitosamente, "False" de lo contrario.

   Distinto en la versión 3.2: El parámetro *timeout* es nuevo.

   Distinto en la versión 3.2: La adquisición de candados ahora puede
   ser interrumpida por señales en POSIX.

lock.release()

   Libera el candado. El candado debe haber sido adquirido
   previamente, pero no necesariamente por el mismo hilo.

lock.locked()

   Retorna el estado del candado: "True" si ha sido adquirido por
   algún hilo, "False" de lo contrario.

Además de estos métodos, los objetos candado pueden ser utilizados
mediante la declaración "with", por ejemplo:

   import _thread

   a_lock = _thread.allocate_lock()

   with a_lock:
       print("a_lock is locked while this executes")

**Salvedades:**

* Los hilos interactúan de manera extraña con interrupciones: la
  excepción "KeyboardInterrupt" va a ser recibida por un hilo
  cualquiera. (Cuando el módulo "signal" está disponible, la
  interrupción siempre se dirige al hilo principal.

* Invocar a "sys.exit()" o lanzar la excepción "SystemExit" equivale a
  invocar "_thread.exit()".

* It is not possible to interrupt the "acquire()" method on a lock ---
  the "KeyboardInterrupt" exception will happen after the lock has
  been acquired.

* Cuando el hilo principal sale, ¿sobreviven los otros hilos? Depende
  de cómo esté definido por el sistema. En la mayoría de los sistemas,
  los hilos se cierran inmediatamente (*killed*), sin ejecutar las
  cláusulas "try" ... "finally" o los destructores del objeto.

* Cuando el hilo principal sale, no hace ninguna de las tareas de
  limpieza habituales (excepto que se haga honor a las cláusulas "try"
  ... "finally"), y los archivos de E/S estándar no son liberados.
