当前位置: 首页 > 科技观察

每日一技能:等待多个线程同时结束的两种方式

时间:2023-03-16 22:26:26 科技观察

我们在编写多线程代码的时候,可能需要等待多个线程同时结束,然后再进行后续过程。比如我做了一个聚合搜索引擎。当用户输入关键词时,我需要同时在很多搜索引擎上进行搜索,然后将搜索结果汇总返回给用户。示例代码如下:@app.get('/api/search')defsearch(keyword:str):google_result=requests.get('谷歌搜索地址').textbaidu_result=requests.get('百度搜索地址').textbing_result=requests.get('Bing搜索地址').textresult=combine(google_result,baidu_result,bing_result)return{'success':True,'result':result}从上面的代码中,你可能会发现一个问题,即请求多个搜索引擎时,是串行的。先访问Google,访问完再访问百度,访问完最后访问Bing。这显然浪费了很多时间。如果你不知道async/await,那么为了解决这个问题,你唯一能想到的就是使用多线程。使用3个线程同时访问google、baidu和bing,然后将结果传入combine函数。那不就解决问题了吗?如果只是开启多线程,那么方法很简单:importthreadingdefget_url(url):result=requests。get(url,headers=HEADERS).textreturnresult@app.get('/api/search')defsearch(keyword:str):google_thead=threading.Thread(target=get_url,'谷歌搜索地址')baidu_thread=threading.Thread(target=get_url,'百度搜索地址')bing_thread=threading.Thread(target=get_url,'Bing搜索地址')google_thread.start()baidu_thread.start()bing_thread.start()...现在问题来了,三个线程确实已经启动了,但是怎么知道所有的线程都运行完了呢?这里我们给出几种方法。使用join调用线程的.join()方法,可以卡住主线程,直到子线程运行完毕,主线程才能继续运行后面的代码。所以我们可以修改代码为:importthreadingdefget_url(url):result=requests.get(url,headers=HEADERS).textreturnresult@app.get('/api/search')defsearch(keyword:str):google_thead=threading。Thread(target=get_url,'谷歌搜索地址')baidu_thread=threading.Thread(target=get_url,'百度搜索地址')bing_thread=threading.Thread(target=get_url,'Bing搜索地址')google_thread.start()baidu_thread.start()bing_thread.start()google_thread.join()baidu_thread.join()bing_thread.join()但是等一下,我如何获取子线程的返回?默认情况下,您确实无法获取返回数据。所以你需要传递一些东西给子线程来接收结果。所以代码可以改成:importthreadingdefget_url(url,output):result=requests.get(url,headers=HEADERS).textoutput.append(result)@app.get('/api/search')defsearch(keyword:str):result=[]google_thead=threading.Thread(target=get_url,args=['谷歌搜索地址',result])baidu_thread=threading.Thread(target=get_url,args=['百度搜索地址',result])bing_thread=threading.Thread(target=get_url,args=['Bing搜索地址',result])google_thread.start()baidu_thread.start()bing_thread.start()google_thread.join()baidu_thread.join()bing_thread.join()combine(*result)因为线程共享内存,所以可以直接修改主线程传入的列表。使用.join()时需要注意不要把.join()放错地方,否则你的多线程会变成单线程。有关详细信息,您可以阅读我的这篇文章:等一下,不要再搞乱你的多线程连接了。ThreadPoolExecutorPython自带了一个concurrent模块,专门用来处理并发问题。我们也可以使用这个模块中的ThreadPoolExecutor来解决问题:fromconcurrent.futuresimportThreadPoolExecutor,as_completeddefget_url(url):result=requests.get(url,headers=HEADERS).textreturnresult@app.get('/api/search')defsearch(关键字:str):tasks=[]withThreadPoolExecutor()asexecutor:forurlin['谷歌搜索地址','百度搜索地址','必应搜索地址']task=executor.submit(get_url,url)tasks.append(task)result=[x.result()forxinas_completed(tasks)]combine(*result)...concurrent.futures中的as_completed函数接收一个列表,其中包含多个并发任务。当所有并发任务完成运行时,它返回一个可迭代对象。遍历之后,每个元素的.result()就是每个子线程运行的返回结果。其他方法除了上面两种方法,还可以使用multiprocessing.dummy中的Pool来实现更简单的多线程。本文转载自微信公众号“闻所未闻的密码”,可通过以下二维码关注。转载本文请联系Code公众号。