Python 3.7 a ajouté le module contextvars (https://docs.python.org/ja/3/library/contextvars.html) et a introduit la classe ContextVar pour asyncio.
Comme Thread Local (en Python, threading.local ()) qui peut avoir des données uniques pour chaque thread. De plus, la fonctionnalité de ContextVar est que chaque collout peut avoir des données uniques.
Lorsque j'ai utilisé ContextVar, j'ai rencontré un phénomène selon lequel l'ensemble de données de ContextVar avait disparu, j'ai donc réécrit un code de vérification et étudié le comportement.
Dans le code ci-dessous, nous exécutons deux fonctions collout, parent_await_coroutine ()
et parent_create_new_task ()
, et dans chaque fonction, nous définissons la valeur sur ContextVar et appelons la fonction child ()
.
Cette fonction child ()
modifie la valeur extraite de ContextVar.
Les deux fonctions parents appellent la fonction enfant différemment. Le premier attend la coroutine et le second crée et exécute une nouvelle tâche qui encapsule la coroutine.
Lorsqu'elle est exécutée en tant que nouvelle nouvelle tâche, certaines des modifications apportées à la ContextVar dans la fonction enfant ne sont pas reflétées dans la fonction parent. Plus précisément, la fonction enfant ajoute la valeur de ContextVar number_var
et la réinitialise, mais la fonction parent ne lit pas la modification (comme elle l'était avant d'appeler la fonction enfant).
D'autre part, les modifications apportées à la ContextVar msg_var
en objets Msg sont également visibles par la fonction parent.
En effet, le contenu des contextvars a été copié lors de la création de la nouvelle tâche. Vous pouvez le lire sur PEP 567.
Dans ce processus de copie, si l'int de number_var
est int, la valeur est copiée et l'objet Msg de msg_var
est référencé copié (c'est-à-dire Copie superficielle), il est donc considéré que le comportement ci-dessus est effectué. ..
import asyncio
import contextvars
class Msg:
"""Juste une classe de conteneur de texte.
Utilisé pour vérifier la copie peu profonde des contextvars.
"""
def __init__(self, text: str):
self._text = text
@property
def text(self) -> str:
return self._text
@text.setter
def text(self, val):
self._text = val
msg_var: contextvars.ContextVar[Msg] = contextvars.ContextVar('msg_var')
number_var: contextvars.ContextVar[int] = contextvars.ContextVar('number_var')
async def child():
#Obtenez le numéro de ContextVar et ajoutez 1
n = number_var.get()
print(f'child: number={n}') # child: number=1
n += 1
number_var.set(n)
#Obtenez l'objet Msg de ContextVar et modifiez le texte
msg = msg_var.get()
print(f'child: msg="{msg.text}"') # child: msg="msg created by parent"
msg.text = 'msg changed by child'
#Cet enfant()Traitement pour rendre la fonction asynchrone
await asyncio.sleep(0.1)
async def parent_await_coroutine():
n = 1
number_var.set(n)
m = Msg('msg created by parent')
msg_var.set(m)
print(f'parent: number={n}') # parent: number=1
print(f'parent: msg="{m.text}"') # parent: msg="msg created by parent"
await child()
n = number_var.get()
m = msg_var.get()
print(f'parent: number={n}') # parent: number=2
print(f'parent: msg="{m.text}"') # parent: msg="msg changed by child"
async def parent_create_new_task():
n = 1
number_var.set(n)
m = Msg('msg created by parent')
msg_var.set(m)
print(f'parent: number={n}') # parent: number=1
print(f'parent: msg="{m.text}"') # parent: msg="msg created by parent"
await asyncio.create_task(child())
n = number_var.get()
m = msg_var.get()
print(f'parent: number={n}') # parent: number=1
print(f'parent: msg="{m.text}"') # parent: msg="msg changed by child"
if __name__ == '__main__':
asyncio.run(parent_create_new_task())
asyncio.run(parent_await_coroutine())
Recommended Posts