并发服务器的信号传递,使服务器支持并发、GIL全局解释器锁、死锁和Rlock、信号量、event事件、...
服務(wù)器的并發(fā)實(shí)現(xiàn):
服務(wù)端:
importsocketfrom threading importThread"""服務(wù)端
1.要有固定的IP和PORT
2.24小時(shí)不間斷提供服務(wù)
3.能夠支持并發(fā)"""server=socket.socket()
server.bind((‘127.0.0.1‘,8080))
server.listen(5)deftalk(conn):whileTrue:try:
data= conn.recv(1024)if len(data) == 0:break
print(data.decode(‘utf-8‘))
conn.send(data.upper())exceptConnectionResetError as e:print(e)breakconn.close()"""當(dāng)有客戶端過(guò)來(lái)連接的時(shí)候,先執(zhí)行while循環(huán)中的代碼,
開(kāi)啟一個(gè)線程去服務(wù)這個(gè)客戶端,當(dāng)有客戶端再過(guò)來(lái)連的時(shí)候,
可以再開(kāi)啟一個(gè)線程去服務(wù)一個(gè)客戶端"""
whileTrue:
conn, addr= server.accept() #監(jiān)聽(tīng) 等待客戶端的連接 阻塞態(tài)
print(addr)
t= Thread(target=talk, args=(conn,))
t.start()
View Code
客戶端:
importsocket
client=socket.socket()
client.connect((‘127.0.0.1‘,8080))whileTrue:
client.send(b‘hello‘)
data= client.recv(1024)print(data.decode(‘utf-8‘))
View Code
GIL全局解釋器鎖:
Python代碼的執(zhí)行由Python虛擬機(jī)(也叫解釋器主循環(huán))來(lái)控制。Python在設(shè)計(jì)之初就考慮到要在主循環(huán)中,同時(shí)只有一個(gè)線程在執(zhí)行。
雖然 Python 解釋器中可以“運(yùn)行”多個(gè)線程,但在任意時(shí)刻只有一個(gè)線程在解釋器中運(yùn)行。
對(duì)Python虛擬機(jī)的訪問(wèn)由全局解釋器鎖(GIL)來(lái)控制,正是這個(gè)鎖能保證同一時(shí)刻只有一個(gè)線程在運(yùn)行。
GIL本質(zhì)也是一把互斥鎖:將并發(fā)變成串行犧牲效率保證數(shù)據(jù)的安全
用來(lái)阻止同一個(gè)進(jìn)程下的多個(gè)線程的同時(shí)執(zhí)行(同一個(gè)進(jìn)程內(nèi)多個(gè)線程無(wú)法實(shí)現(xiàn)并行但是可以實(shí)現(xiàn)并發(fā))
GIL的存在是因?yàn)镃Python解釋器的內(nèi)存管理不是線程安全的
如果同時(shí)運(yùn)行,那么線程中的值可能還沒(méi)有被使用就被垃圾回收機(jī)制進(jìn)行回收了,所以CPython解釋器的內(nèi)存管理不是線程安全的,要有GIL。
垃圾回收機(jī)制
1.引用計(jì)數(shù)
2.標(biāo)記清除
3.分代回收
研究python的多線程是否有用需要分情況討論:
四個(gè)任務(wù) 計(jì)算密集型的
單核情況下
開(kāi)線程更省資源
多核情況下
開(kāi)進(jìn)程
開(kāi)線程
四個(gè)任務(wù)IO密集型的
單核情況下
開(kāi)線程更節(jié)省資源
多核情況下
開(kāi)線程更節(jié)省資源
代碼驗(yàn)證:
計(jì)算密集型:
計(jì)算密集型from multiprocessing importProcessfrom threading importThreadimportos,timedefwork():
res=0for i in range(100000000):
res*=iif __name__ == ‘__main__‘:
l=[]print(os.cpu_count()) #本機(jī)為6核
start=time.time()for i in range(6):#p=Process(target=work) #耗時(shí) 4.732933044433594
p=Thread(target=work) #耗時(shí) 22.83087730407715
l.append(p)
p.start()for p inl:
p.join()
stop=time.time()print(‘run time is %s‘ %(stop-start))
View Code
IO密集型:
from multiprocessing importProcessfrom threading importThreadimportthreadingimportos,timedefwork():
time.sleep(2)if __name__ == ‘__main__‘:
l=[]print(os.cpu_count()) #本機(jī)為6核
start=time.time()for i in range(40):#p=Process(target=work) #耗時(shí)9.001083612442017s多,大部分時(shí)間耗費(fèi)在創(chuàng)建進(jìn)程上
p=Thread(target=work) #耗時(shí)2.051966667175293s多
l.append(p)
p.start()for p inl:
p.join()
stop=time.time()print(‘run time is %s‘ %(stop-start))
View Code
死鎖和Rlock:
死鎖狀態(tài):也就是不同線程都去等待對(duì)方去釋放鎖的情況(互相僵持)
建議:輕易不要去嘗試處理鎖的問(wèn)題。
from threading importThread,Lock,current_thread,RLockimporttime"""Rlock可以被第一個(gè)搶到鎖的人連續(xù)的acquire和release
每acquire一次鎖身上的計(jì)數(shù)加1
每release一次鎖身上的計(jì)數(shù)減1
只要鎖的計(jì)數(shù)不為0 其他人都不能搶"""
#mutexA = Lock()#mutexB = Lock()
mutexA = mutexB = RLock() #A B現(xiàn)在是同一把鎖
classMyThread(Thread):def run(self): #創(chuàng)建線程自動(dòng)觸發(fā)run方法 run方法內(nèi)調(diào)用func1 func2相當(dāng)于也是自動(dòng)觸發(fā)
self.func1()
self.func2()deffunc1(self):
mutexA.acquire()print(‘%s搶到了A鎖‘%self.name) #self.name等價(jià)于current_thread().name
mutexB.acquire()print(‘%s搶到了B鎖‘%self.name)
mutexB.release()print(‘%s釋放了B鎖‘%self.name)
mutexA.release()print(‘%s釋放了A鎖‘%self.name)deffunc2(self):
mutexB.acquire()print(‘%s搶到了B鎖‘%self.name)
time.sleep(1)
mutexA.acquire()print(‘%s搶到了A鎖‘ %self.name)
mutexA.release()print(‘%s釋放了A鎖‘ %self.name)
mutexB.release()print(‘%s釋放了B鎖‘ %self.name)for i in range(10):
t=MyThread()
t.start()
View Code
信號(hào)量:
過(guò)程:
先有五個(gè)線程同時(shí)獲得鎖,然后運(yùn)行完因?yàn)椴煌€程睡眠時(shí)間不同所以釋放時(shí)間也會(huì)不同,
之后先釋放出來(lái)的鎖便可以讓其他線程再去搶奪。
#信號(hào)量可能在不同的領(lǐng)域中 對(duì)應(yīng)不同的知識(shí)點(diǎn)
"""互斥鎖:一個(gè)廁所(一個(gè)坑位)
信號(hào)量:公共廁所(多個(gè)坑位)"""
from threading importSemaphore,Threadimporttimeimportrandom
sm= Semaphore(5) #造了一個(gè)含有五個(gè)的坑位的公共廁所
deftask(name):
sm.acquire()print(‘%s占了一個(gè)坑位‘%name)
time.sleep(random.randint(1,3))
sm.release()for i in range(40):
t= Thread(target=task,args=(i,))
t.start()
View Code
envent事件:
from threading importEvent, Threadimporttime#先生成一個(gè)event對(duì)象
e =Event()deflight():print(‘紅燈正亮著‘)
time.sleep(3)
e.set()#發(fā)信號(hào)
print(‘綠燈亮了‘)defcar(name):print(‘%s正在等紅燈‘%name)
e.wait()#等待信號(hào)
print(‘%s加油門(mén)飆車(chē)了‘%name)
t= Thread(target=light)
t.start()for i in range(10):
t= Thread(target=car,args=(‘傘兵%s‘%i,))
t.start()"""先啟一個(gè)線程,打印路燈亮了,進(jìn)入睡眠的同時(shí),起了十個(gè)線程,之后打印正在等綠燈,
之后等待e.wait()收到信號(hào),當(dāng)運(yùn)行e.set()發(fā)信號(hào)時(shí),便可以繼續(xù)運(yùn)行了。"""
View Code
線程queue:
importqueue"""同一個(gè)進(jìn)程下的多個(gè)線程本來(lái)就是數(shù)據(jù)共享 為什么還要用隊(duì)列
因?yàn)殛?duì)列是管道+鎖 使用隊(duì)列你就不需要自己手動(dòng)操作鎖的問(wèn)題
因?yàn)殒i操作的不好極容易產(chǎn)生死鎖現(xiàn)象"""
#q = queue.Queue()#q.put(‘hahha‘)#print(q.get())
#q = queue.LifoQueue()#后進(jìn)先出#q.put(1)#q.put(2)#q.put(3)#print(q.get())
#q = queue.PriorityQueue()## 數(shù)字越小 優(yōu)先級(jí)越高#q.put((10,‘haha‘))#q.put((100,‘hehehe‘))#q.put((0,‘xxxx‘))#q.put((-10,‘yyyy‘))#print(q.get())
View Code
原文:https://www.cnblogs.com/yangjiaoshou/p/11353187.html
總結(jié)
以上是生活随笔為你收集整理的并发服务器的信号传递,使服务器支持并发、GIL全局解释器锁、死锁和Rlock、信号量、event事件、...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计算机学习的基本形式,电脑基础知识学习方
- 下一篇: dns的服务器地址是多少当前位置,dns