Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identificador automático de sección crítica en Concurrencia #193

Open
5 tasks done
3rdPix opened this issue Sep 14, 2022 · 1 comment
Open
5 tasks done

Identificador automático de sección crítica en Concurrencia #193

3rdPix opened this issue Sep 14, 2022 · 1 comment
Assignees
Labels
Contenidos Dudas sobre contenidos

Comments

@3rdPix
Copy link

3rdPix commented Sep 14, 2022

Prerrequisitos

(Marcar colocando una X entre los corchetes los ítems que ya hiciste, así: "[X]")

  • Leí las reglas del foro (Instalación py 3.11.X con X>=7 Syllabus#1).
  • Busqué en las issues si ya preguntaron mi duda y no encontré nada parecido (https://github.com/IIC2233/syllabus/issues).
  • Mi duda no se trata de un tema administrativo o personal, ya que en ese caso debo contactar a mi profe, al Jefe de Bienestar o al correo del curso (iic2233@ing.puc.cl).
  • Utilizaré un título descriptivo y llenaré correctamente esta plantilla.
  • De ser necesario, colocaré código simple que permita explicar mi problema o duda.

Duda

En los contenidos de Concurrencia, el primer y principal ejemplo que se propone trabaja aumentando un contador a través de diferentes hilos (threads) simultáneamente. Y a partir de ahí se construye todo el razonamiento.

Sin embargo, cuando intento ejecutar el código (que debería contener el problema), no me aparece ningún resultado problemático. En particular:

import threading


class Contador: 
    def __init__(self):
        self.valor = 0


def sumador(contador):
    for _ in range(10**6):   ### Este valor lo aumenté hasta 10^10
        contador.valor += 1

contador = Contador()        
t1 = threading.Thread(target=sumador, args=(contador,))
t2 = threading.Thread(target=sumador, args=(contador,))

t1.start()
t2.start()
t1.join()
t2.join()

print("Listo, nuestro contador vale", contador.valor)

Cuyo output en el ejemplo es: Listo, nuestro contador vale 1921644. A mi me resulta el número perfectamente, es decir, mi output es 2000000.

Entiendo perfectamente que quizás no debería darme el mismo número que en el ejemplo. Pero es que tampoco me da ningún número erróneo cuando intento poner a prueba al computador. Aumenté sucesivamente los ordenes de magnitud hasta obligarlo a contar hasta 10^10 con cada hilo, y el resultado se mantenía en su perfecto 20000000000.

De aquí surge la pregunta; ¿A qué se puede deber este comportamiento? ¿Suerte que cada sección de línea de código crítico haya calzado en el orden que el OS decidió darle a los hilos? ¿O acaso existe algún verificador interno que de forma automática identifique cuales son las secciones críticas que deben estar sincronizadas? ¿Tiene que ver el poder computacional que tiene la máquina en evitar que surja un error?

@MrNachoX MrNachoX self-assigned this Sep 14, 2022
@MrNachoX
Copy link

Hola @3rdPix !

Buena pregunta, este comportamiento ocurre en las nuevas versiones de python.
Python 3.10.7:

Listo, nuestro contador vale 2000000

Python 3.8.9

Listo, nuestro contador vale 1234477

Pero puedes modificar el código a esto:

def sumador(contador):
    for _ in range(10**6):
        contador.valor += int(1)

para volver a obtener el resultado problemático.

En resumen, esto se debe a que se implementó un cambio que hace que la operación += sea más rápida y ahora no ocurre nunca que dos operaciones de este tipo se ejecutan al mismo tiempo.

En términos más técnicos, lo que sucede es que por limitaciones de python en realidad nunca se hace uso de múltiples nucleos del procesador para ejecutar código al mismo tiempo. Lo que sí se hace es cambiar de thread a thread de manera muy rápida para dar la experiencia de concurrencia. Puedes leer más acerca de como funciona esto en los contenidos de la semana 5, pero en resumen se siguen una serie de pasos:

  1. Se escoge un thread entre los que están disponible para ejecutar.
  2. Se ejecuta un cierto número de instrucciones de ese thread, o durante un cierto tiempo.
  3. Se deja el thread actual en espera.
  4. Se vuelve al paso 1.

Las modificaciones implementadas en las nuevas versiones de python provocan que el cambio de threads nunca ocurra durante esta operación de suma, entonces no se producen problemas en el resultado final. Si tienes curiosidad, puedes ver la respuesta de uno de los desarrolladores de python aquí y como python es de código abierto, incluso puedes ver el pull request y el commit específico donde se implementaron estos cambios 😲.

@MrNachoX MrNachoX added the Contenidos Dudas sobre contenidos label Sep 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Contenidos Dudas sobre contenidos
Projects
None yet
Development

No branches or pull requests

2 participants