Asyncio 意外条件竞争问题
Asyncio unexpected condition race problem
我在使用 asyncio 和 aiohttp 时遇到了一个奇怪的问题。我可能遇到了竞争条件。我 运行 这个异步函数 cvim_vlan_list 两次来自具有不同 x 变量的循环。但是我遇到的是,我从函数中的第一个 await 获得的令牌在代码中间以某种方式被重写了——意思是 cvim_headers 对象是相同的,但不应该是。我把注释放在代码旁边。我真的不知道为什么要重写这个值。
正在初始化循环
self.loop = asyncio.new_event_loop()
checks_to_run = self.returnChecksBasedOnInputs()
self.loop.run_until_complete(asyncio.wait_for(self.run_all_checks_async(checks_to_run),180))
asyncio.set_event_loop(self.loop)
在我传递循环的文件中
x = 0
cvim_tasks = []
for cvim_scan_container in self.constants.cvim_scan_containers:
cvim_tasks.append(self.loop.create_task(self.cvim_vlan_list(cvim_scan_container,x)))
x += 1
await asyncio.gather(*cvim_tasks)
async def cvim_vlan_list(self, cvim_scan_container, x):
cvim_vlans = []
network_ids = []
# 2. Get cvim_container_token for each az
cvim_headers = self.constants.cvim_headers
cvimAuthenticationData="{\"auth\": {\"methods\": [\"credentials\"], \"credentials\": {\"username\": \"calipso\",\"password\": \"" + self.constants.azpasswords[x] + "\"}}}\r\n"
response = await apiCaller().callAndReturnJson("POST",f"https://{cvim_scan_container}/auth/tokens",headers=cvim_headers, session=self.session,payload=cvimAuthenticationData,log=self.log)
cvim_container_token = response['token']
#update headers with token
cvim_headers['X-Auth-token'] = cvim_container_token ############## HERE ARE cvim_headers different
# 3. RUN SCAN in each az
scanData="{\"env_name\" : \"cvim-fci-pod"+str(x+1)+"-muc-eip\",\r\n\"log_level\": \"warning\"\r\n}"
response = await apiCaller().callAndReturnJson("POST",f"https://{cvim_scan_container}/scans",headers=cvim_headers, session=self.session,payload=scanData,log=self.log)
scan_id = response['id']
############## HERE ARE cvim_headers the same
############## The token from second def execution is also in the first def execution, that means cvim_headers are same which results to error from API
# 4. Periodically check if scan is completed
isScanRunning = True
while isScanRunning == True:
self.log.info('checking scan')
self.log.info(cvim_headers) ######## HERE THE token from second def execution is also in the first def execution, that means cvim_headers are same which results to error from API
response = await apiCaller().callAndReturnJson("GET",f"https://{cvim_scan_container}/scans?env_name=cvim-fci-pod"+str(x+1)+"-muc-eip&id="+scan_id,headers=cvim_headers, session=self.session,payload="",log=self.log)
self.log.info(response)
if response['status'] == 'completed':
self.log.info('Scan completed')
isScanRunning = False
else:
await asyncio.sleep(5)
您正在使用名为 self.constants.cvim_headers
的东西。大概这是一本字典(包含一些默认值headers)。在 cvim_vlan_list
中你用它来做这个
cvim_headers = self.constants.cvim_headers
然后你用
更新它
cvim_container_token = response['token']
cvim_headers['X-Auth-token'] = cvim_container_token
我认为你遇到的问题只是对这里发生的事情的误解。赋值没有将 self.constants.cvim_headers
的副本放入 cvim_headers
,而是将 self.constants.cvim_headers
引用的字典的引用放入 cvim_headers
。每次分配给 X-Auth-token
键时,您都会更改 cvim_headers
和 self.constants.cvim_headers
引用的字典,覆盖与该键关联的先前值。
为防止这种情况发生,您需要创建默认 headers 的副本并将其分配给它。
cvim_headers = self.constants.cvim_headers.copy()
我在使用 asyncio 和 aiohttp 时遇到了一个奇怪的问题。我可能遇到了竞争条件。我 运行 这个异步函数 cvim_vlan_list 两次来自具有不同 x 变量的循环。但是我遇到的是,我从函数中的第一个 await 获得的令牌在代码中间以某种方式被重写了——意思是 cvim_headers 对象是相同的,但不应该是。我把注释放在代码旁边。我真的不知道为什么要重写这个值。
正在初始化循环
self.loop = asyncio.new_event_loop()
checks_to_run = self.returnChecksBasedOnInputs()
self.loop.run_until_complete(asyncio.wait_for(self.run_all_checks_async(checks_to_run),180))
asyncio.set_event_loop(self.loop)
在我传递循环的文件中
x = 0
cvim_tasks = []
for cvim_scan_container in self.constants.cvim_scan_containers:
cvim_tasks.append(self.loop.create_task(self.cvim_vlan_list(cvim_scan_container,x)))
x += 1
await asyncio.gather(*cvim_tasks)
async def cvim_vlan_list(self, cvim_scan_container, x):
cvim_vlans = []
network_ids = []
# 2. Get cvim_container_token for each az
cvim_headers = self.constants.cvim_headers
cvimAuthenticationData="{\"auth\": {\"methods\": [\"credentials\"], \"credentials\": {\"username\": \"calipso\",\"password\": \"" + self.constants.azpasswords[x] + "\"}}}\r\n"
response = await apiCaller().callAndReturnJson("POST",f"https://{cvim_scan_container}/auth/tokens",headers=cvim_headers, session=self.session,payload=cvimAuthenticationData,log=self.log)
cvim_container_token = response['token']
#update headers with token
cvim_headers['X-Auth-token'] = cvim_container_token ############## HERE ARE cvim_headers different
# 3. RUN SCAN in each az
scanData="{\"env_name\" : \"cvim-fci-pod"+str(x+1)+"-muc-eip\",\r\n\"log_level\": \"warning\"\r\n}"
response = await apiCaller().callAndReturnJson("POST",f"https://{cvim_scan_container}/scans",headers=cvim_headers, session=self.session,payload=scanData,log=self.log)
scan_id = response['id']
############## HERE ARE cvim_headers the same
############## The token from second def execution is also in the first def execution, that means cvim_headers are same which results to error from API
# 4. Periodically check if scan is completed
isScanRunning = True
while isScanRunning == True:
self.log.info('checking scan')
self.log.info(cvim_headers) ######## HERE THE token from second def execution is also in the first def execution, that means cvim_headers are same which results to error from API
response = await apiCaller().callAndReturnJson("GET",f"https://{cvim_scan_container}/scans?env_name=cvim-fci-pod"+str(x+1)+"-muc-eip&id="+scan_id,headers=cvim_headers, session=self.session,payload="",log=self.log)
self.log.info(response)
if response['status'] == 'completed':
self.log.info('Scan completed')
isScanRunning = False
else:
await asyncio.sleep(5)
您正在使用名为 self.constants.cvim_headers
的东西。大概这是一本字典(包含一些默认值headers)。在 cvim_vlan_list
中你用它来做这个
cvim_headers = self.constants.cvim_headers
然后你用
更新它cvim_container_token = response['token']
cvim_headers['X-Auth-token'] = cvim_container_token
我认为你遇到的问题只是对这里发生的事情的误解。赋值没有将 self.constants.cvim_headers
的副本放入 cvim_headers
,而是将 self.constants.cvim_headers
引用的字典的引用放入 cvim_headers
。每次分配给 X-Auth-token
键时,您都会更改 cvim_headers
和 self.constants.cvim_headers
引用的字典,覆盖与该键关联的先前值。
为防止这种情况发生,您需要创建默认 headers 的副本并将其分配给它。
cvim_headers = self.constants.cvim_headers.copy()