from jyquickhelper import add_notebook_menu
add_notebook_menu()

"""
Explanation: Le GIL
Le GIL ou Global Interpreter Lock est un verrou unique auquel l'interpréteur Python fait appel constamment pour protéger tous les objets qu'il manipule contre des accès concurrentiels.
End of explanation
"""


def create_list(n):
    res = []
    for i in range(n):
        res.append(i)
    return res

%timeit create_list(100000)

"""
Explanation: Deux listes en parallèlle
On mesure le temps nécessaire pour créer deux liste et comparer ce temps avec celui que cela prendrait en parallèle.
End of explanation
"""


from concurrent.futures import ThreadPoolExecutor

def run2(nb):
    with ThreadPoolExecutor(max_workers=2) as executor:
        for res in executor.map(create_list, [nb, nb+1]):
            pass
            
%timeit run2(100000)

"""
Explanation: En parallèle avec le module concurrent.futures et deux appels à la même fonction.
End of explanation
"""


import time

def attendre(t=0.009):
    time.sleep(t)
    return None

%timeit attendre()

def run2(t):
    with ThreadPoolExecutor(max_workers=2) as executor:
        for res in executor.map(attendre, [t, t+0.001]):
            pass
            
%timeit run2(0.009)

"""
Explanation: C'est plus long que si les calculs étaient lancés les uns après les autres. Ce temps est perdu à synchroniser les deux threads bien que les deux boucles n'aient rien à échanger. Chaque thread passe son temps à attendre que l'autre ait terminé de mettre à jour sa liste et le GIL impose que ces mises à jour aient lieu une après l'autre.
Un autre scénario
Au lieu de mettre à jour une liste, on va lancer un thread qui ne fait rien qu'attendre. Donc le GIL n'est pas impliqué.
End of explanation
"""


