日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

浅谈python异步IO,同步IO,线程与进程~

發(fā)布時(shí)間:2025/3/15 python 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈python异步IO,同步IO,线程与进程~ 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

主線程下的線程之間是可以通信的,但是父進(jìn)程下的子進(jìn)程之間不能主動(dòng)通信,但是子進(jìn)程想要實(shí)現(xiàn)通信也是可以的,可以選擇折中的方法來(lái)實(shí)現(xiàn),比如multiprocessing.Queue,用法與線程中的queue基本一致,直接上例子:

import threading from multiprocessing import Process,Queue import time def thre(qq):qq.put([1,'xixi',2])if __name__ =='__main__':q = Queue()p = Process(target=thre,args=(q,))#進(jìn)程中,因?yàn)檫M(jìn)程內(nèi)存是獨(dú)立的,所以不能相互調(diào)用,必須傳入?yún)?shù),這個(gè)q其實(shí)是復(fù)制了一份Queue的實(shí)例,如果和線程一樣不傳參數(shù),就會(huì)報(bào)錯(cuò)** not find。。。因?yàn)閮?nèi)存不共享。 #p =threading.Thread(target=thre) #線程中,不傳參數(shù)是可以調(diào)用函數(shù)thre,因?yàn)樗麄兪窃谕粋€(gè)內(nèi)存地址下操作【上面改為def thre():】,當(dāng)然傳參數(shù)也沒(méi)問(wèn)題。 p.start()print(q.get())#進(jìn)程之間想要有聯(lián)系,主動(dòng)無(wú)法聯(lián)系,這是硬傷,就如qq和word一樣,但是如果非要他們有聯(lián)系,就是從word復(fù)制文字到qq里(或者qq復(fù)制圖片文字到word里面這樣),這樣貌似兩者有聯(lián)系,實(shí)際上只是克隆了那段文字的關(guān)系,但是看起來(lái)好像就有聯(lián)系了,那么python中process之間的通信就是可以考慮通過(guò) Queue來(lái)實(shí)現(xiàn),Queue內(nèi)部操作其實(shí)就是通過(guò)pickle的功能來(lái)實(shí)現(xiàn)傳參數(shù)等各種聯(lián)系的 還有一個(gè)是pipe:通過(guò)管道來(lái)傳遞,也是建立一個(gè)pipe的實(shí)例化對(duì)象。 from multiprocessing import Process,Pipedef f(conn):conn.send('balabala')print(conn.recv())if __name__=='__main__':parent_conn,child_conn=Pipe()#實(shí)例化后是返回兩個(gè)值,一個(gè)是父接頭一個(gè)是子接頭,因?yàn)槭枪艿馈?/span>p = Process(target=f,args=(child_conn,))p.start()print(parent_conn.recv())parent_conn.send('babababa')

這樣也能實(shí)現(xiàn)數(shù)據(jù)的傳遞,但都不是共享

進(jìn)程之間要實(shí)現(xiàn)共享,需要用manager。

from multiprocessing import Process,Manager import time,osdef thre(dd,ll):dd[os.getpid()] = os.getppid()ll.append(os.getpid())print(ll)print(dd)if __name__ =='__main__':manager = Manager()d = manager.dict()l = manager.list(range(3))t_list = []for i in range(10):p = Process(target=thre,args=(d,l))p.start()t_list.append(p)for res in t_list:res.join()#等待的意思

此時(shí)字典 d 和 列表 l,他們的數(shù)據(jù)同時(shí)都可以被進(jìn)程修改覆蓋,只不過(guò)我這里用的是os.getpid()獲取的數(shù)據(jù)不一致,如果是一致的,那么最終字典只有一個(gè)k-v,列表是10個(gè)一樣的數(shù)據(jù)。

?

進(jìn)程鎖的存在是為了輸出同一個(gè)屏幕不要亂。。。僅此而已

?

進(jìn)程池的作用和線程中的信號(hào)量差不多,同一時(shí)間允許幾個(gè)進(jìn)程同時(shí)運(yùn)行


其中有 apply 和apply_async,一個(gè)是串行操作,一個(gè)是并行操作。

from multiprocessing import Pool import time,osdef thre(dd):time.sleep(1)print('the process:',os.getpid())return dd+100 def g(c):print('haha',c,os.getpid())#start_time = time.time() # l=[] if __name__ =='__main__':p_ = Pool(3)#允許同時(shí)運(yùn)行的進(jìn)程數(shù)為3。print(os.getpid())for i in range(10):p_.apply_async(func=thre,args=(i,),callback=g)#【callback是回調(diào)函數(shù),傳的參數(shù)是thre的返回值】 p_.close()p_.join()#這里如果不加join,在并行中會(huì)直接close,程序會(huì)直接關(guān)閉,加了join,主進(jìn)程就會(huì)等待子進(jìn)程結(jié)束以后最后才關(guān)閉,這個(gè)只在并行中有用,串行中沒(méi)有什么作用。一定要先close再join

協(xié)程:可以實(shí)現(xiàn)高并發(fā),本質(zhì)上就是單線程,一個(gè)cpu支持上萬(wàn)個(gè)協(xié)程并發(fā)
gevent(自動(dòng)觸發(fā)) 和 greenlet(手動(dòng)觸發(fā))

import geventdef fun1():print('runing 1 ...')gevent.sleep(2)#模仿ioprint('running 2 ...')def fun2():print('running 3 ...')gevent.sleep(3)print('running 4')def fun3():print('running 5 ...')gevent.sleep(0)print('end?')gevent.joinall([gevent.spawn(fun1),gevent.spawn(fun2),gevent.spawn(fun3)])

運(yùn)行結(jié)果:

runing 1 ...
running 3 ...
running 5 ...
end?
running 2 ...
running 4
----------------------
sleep相當(dāng)于觸發(fā)的按鈕,出現(xiàn)一次sleep,就去找下一個(gè)函數(shù)中的內(nèi)容打印等操作,sleep內(nèi)的時(shí)間相當(dāng)于他卡幾次,sleep(3)相當(dāng)于卡3秒,如果其他已經(jīng)沒(méi)卡著,就馬上執(zhí)行沒(méi)卡著的語(yǔ)句,知道最后回來(lái)等到時(shí)間結(jié)束執(zhí)行最后這個(gè)語(yǔ)句。協(xié)程用于多并發(fā)爬蟲(chóng)中效果很好。

import gevent,time import urllib.request as ul from gevent import monkey monkey.patch_all()#這個(gè)標(biāo)識(shí)代表把所有程序都當(dāng)做io直接切換操作,不加這句話,因?yàn)間event不會(huì)辨認(rèn)出urllib的有io操作,相當(dāng)于串行操作。 def f(url):print('GET %s'%url)res = ul.urlopen(url).read()print('recv bytes %s from %s'%(len(res),url))time_start = time.time() l=['https://www.python.org/','http://km.58.com/','http://kan.sogou.com/dongman/','http://news.sohu.com/'] for i in l:f(i) print('同步時(shí)間:',time.time()-time_start) async_time = time.time() gevent.joinall([gevent.spawn(f,'https://www.python.org/'),gevent.spawn(f,'http://km.58.com/'),gevent.spawn(f,'http://kan.sogou.com/dongman/'),gevent.spawn(f,'http://news.sohu.com/')]) print('異步時(shí)間:',time.time()-async_time)

運(yùn)行結(jié)果:
GET https://www.python.org/
recv bytes 48860 from https://www.python.org/
GET http://km.58.com/
recv bytes 104670 from http://km.58.com/
GET http://kan.sogou.com/dongman/
recv bytes 12713 from http://kan.sogou.com/dongman/
GET http://news.sohu.com/
recv bytes 170935 from http://news.sohu.com/
同步時(shí)間: 3.780085563659668
GET https://www.python.org/
GET http://km.58.com/
GET http://kan.sogou.com/dongman/
GET http://news.sohu.com/
recv bytes 12690 from http://kan.sogou.com/dongman/
recv bytes 170935 from http://news.sohu.com/
recv bytes 104670 from http://km.58.com/
recv bytes 48860 from https://www.python.org/
異步時(shí)間: 2.5934762954711914

?

用戶空間和內(nèi)核空間(kernel)
現(xiàn)在操作系統(tǒng)中都是采用虛擬存儲(chǔ)器,操作系統(tǒng)的核心是內(nèi)核,獨(dú)立于普通的應(yīng)用程序,可以訪問(wèn)受保護(hù)的內(nèi)存空間,也有訪問(wèn)硬件設(shè)備的權(quán)限,為了保證用戶進(jìn)程不能直接操作內(nèi)核(kernel),保證內(nèi)核的安全,操作系統(tǒng)把虛擬空間分為兩部分,一部分為內(nèi)核空間,一部分為用戶空間。

進(jìn)程切換
為了控制進(jìn)程的執(zhí)行,內(nèi)核必須有能力掛起在CPU上運(yùn)行的進(jìn)程,并且恢復(fù)以前掛起的某個(gè)進(jìn)程的執(zhí)行,這種行為稱作進(jìn)程切換,因此,任何進(jìn)程都是在操作系統(tǒng)內(nèi)核的支持下運(yùn)行的,與內(nèi)核緊密相連。
從一個(gè)進(jìn)程的運(yùn)行轉(zhuǎn)到另一個(gè)進(jìn)程上運(yùn)行,其實(shí)就是保存上下文就切換了。下次再來(lái)又從之前保存的位置開(kāi)始。

進(jìn)程的阻塞:
正式執(zhí)行的進(jìn)程,由于期待的某件事情并未發(fā)生,如請(qǐng)求系統(tǒng)資源失敗等待,等待某種操作的完成,新數(shù)據(jù)尚未達(dá)到或無(wú)新工作開(kāi)始等,則有系統(tǒng)自動(dòng)執(zhí)行阻塞原語(yǔ),使自己由原來(lái)的運(yùn)行狀態(tài)轉(zhuǎn)為阻塞狀態(tài)暫停等待()
??梢?jiàn),進(jìn)程的阻塞是進(jìn)程自身的一種主動(dòng)行為,也因此只有處于運(yùn)行狀態(tài)的進(jìn)程(獲得CPU),才可能將其轉(zhuǎn)為阻塞狀態(tài),當(dāng)進(jìn)程進(jìn)入阻塞狀態(tài)時(shí)候,不耗費(fèi)CPU資源的。

緩存I/O
又被成為標(biāo)準(zhǔn)IO,大多數(shù)文件系統(tǒng)默認(rèn)I/O操作都是緩存I/O,在Linux的緩存I/O機(jī)制當(dāng)中,操作系統(tǒng)會(huì)將I/O的數(shù)據(jù)緩存在文件系統(tǒng)的頁(yè)緩存中,也就是說(shuō),文件數(shù)據(jù)會(huì)被拷貝到系統(tǒng)內(nèi)核的緩沖區(qū)中,然后再?gòu)南到y(tǒng)內(nèi)核的緩沖區(qū)拷貝到用戶的進(jìn)程內(nèi)存里也就是應(yīng)用程序的地址空間。缺點(diǎn)就是數(shù)據(jù)會(huì)在用戶進(jìn)程應(yīng)用程序地址空間和內(nèi)核空間反復(fù)拷貝操作,這時(shí)對(duì)于CPU和內(nèi)存的開(kāi)銷很大。


I/O模式
同步IO和異步IO:
同步IO中有:阻塞IO(blocking I/O),非阻塞IO(non-blocking I/O),多路復(fù)用IO(I/O multiplexing) 信號(hào)驅(qū)動(dòng)(實(shí)際中不常用。在此暫時(shí)不記錄筆記)
異步I/O(asynchronous I/O)

阻塞IO:發(fā)起請(qǐng)求,然后等待數(shù)據(jù)準(zhǔn)備(此時(shí)進(jìn)程阻塞等待),直到數(shù)據(jù)準(zhǔn)備好接受時(shí),又到內(nèi)核空間開(kāi)始copy給用戶進(jìn)程,此時(shí)又一次阻塞等待,直到數(shù)據(jù)全部發(fā)給用戶進(jìn)程(客戶端)。

非阻塞IO:發(fā)起請(qǐng)求后,瘋狂發(fā)送驗(yàn)證,數(shù)據(jù)未準(zhǔn)備好時(shí),并不會(huì)阻塞block,而是返回一個(gè)error給用戶進(jìn)程,用戶進(jìn)程會(huì)驗(yàn)證是否error,是就繼續(xù)發(fā)出請(qǐng)求,來(lái)回驗(yàn)證,(此時(shí)由于進(jìn)程沒(méi)有阻塞,還可以干其他事,)不是就到了內(nèi)核空間開(kāi)始copy數(shù)據(jù),此時(shí)其實(shí)還是阻塞,如果數(shù)據(jù)小會(huì)很快,數(shù)據(jù)大還是會(huì)感受到卡。最后用戶收到完整數(shù)據(jù)。

多路復(fù)用I/O:一次發(fā)起幾百次請(qǐng)求鏈接,無(wú)論哪條鏈接有數(shù)據(jù)回復(fù),都會(huì)通知用戶進(jìn)程開(kāi)始接受數(shù)據(jù),此時(shí)那幾條鏈接又開(kāi)始進(jìn)行內(nèi)核copy直到進(jìn)程收到完整數(shù)據(jù)(其實(shí)這里也是阻塞的)。這個(gè)模式的核心其實(shí)是用非阻塞IO的方式來(lái)驅(qū)動(dòng),所以形成多路復(fù)用,在用戶看來(lái)已經(jīng)是多并發(fā)了。

異步I/O:這個(gè)就牛逼了,他發(fā)起請(qǐng)求,當(dāng)場(chǎng)就收到回復(fù)‘去干你其他的事’,此時(shí)該進(jìn)程開(kāi)始其他部分運(yùn)行,并未有任何阻塞,收到數(shù)據(jù)時(shí),直接后臺(tái)開(kāi)始內(nèi)核copy,全部搞完以后直接‘送快遞到家門口’,給一個(gè)信號(hào)通知,用戶進(jìn)程順手就接受了數(shù)據(jù),此時(shí)整個(gè)進(jìn)程根本沒(méi)有任何阻塞過(guò)程!這就是異步IO。

selectors
selectors中涵蓋了select,poll,epoll,詳細(xì)實(shí)例:

import selectors,socket sel = selectors.DefaultSelector()def accept(sock,mask):conn,addr = sock.accept()conn.setblocking(False)sel.register(conn,selectors.EVENT_READ,read)def read(conn,mask):data = conn.recv(1024).decode()if data:conn.send(('haha+%s'%data).encode())else:print('發(fā)什么?',conn)sel.unregister(conn)conn.close()sock = socket.socket() sock.bind(('localhost',5000)) sock.listen(1000) sel.register(sock,selectors.EVENT_READ,accept) while True:events= sel.select()for key,mask in events:callback = key.datacallback(key.fileobj,mask)
這個(gè)可以進(jìn)行多并發(fā)運(yùn)行。

好了 異步同步線程進(jìn)程就到這了,人生苦短,我用python。

轉(zhuǎn)載于:https://www.cnblogs.com/Jason504327775/p/8503516.html

總結(jié)

以上是生活随笔為你收集整理的浅谈python异步IO,同步IO,线程与进程~的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。