并发服务器的信号传递,使服务器支持并发、GIL全局解释器锁、死锁和Rlock、信号量、event事件、...
服務器的并發實現:
服務端:
importsocketfrom threading importThread"""服務端
1.要有固定的IP和PORT
2.24小時不間斷提供服務
3.能夠支持并發"""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()"""當有客戶端過來連接的時候,先執行while循環中的代碼,
開啟一個線程去服務這個客戶端,當有客戶端再過來連的時候,
可以再開啟一個線程去服務一個客戶端"""
whileTrue:
conn, addr= server.accept() #監聽 等待客戶端的連接 阻塞態
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代碼的執行由Python虛擬機(也叫解釋器主循環)來控制。Python在設計之初就考慮到要在主循環中,同時只有一個線程在執行。
雖然 Python 解釋器中可以“運行”多個線程,但在任意時刻只有一個線程在解釋器中運行。
對Python虛擬機的訪問由全局解釋器鎖(GIL)來控制,正是這個鎖能保證同一時刻只有一個線程在運行。
GIL本質也是一把互斥鎖:將并發變成串行犧牲效率保證數據的安全
用來阻止同一個進程下的多個線程的同時執行(同一個進程內多個線程無法實現并行但是可以實現并發)
GIL的存在是因為CPython解釋器的內存管理不是線程安全的
如果同時運行,那么線程中的值可能還沒有被使用就被垃圾回收機制進行回收了,所以CPython解釋器的內存管理不是線程安全的,要有GIL。
垃圾回收機制
1.引用計數
2.標記清除
3.分代回收
研究python的多線程是否有用需要分情況討論:
四個任務 計算密集型的
單核情況下
開線程更省資源
多核情況下
開進程
開線程
四個任務IO密集型的
單核情況下
開線程更節省資源
多核情況下
開線程更節省資源
代碼驗證:
計算密集型:
計算密集型from multiprocessing importProcessfrom threading importThreadimportos,timedefwork():
res=0for i in range(100000000):
res*=iif __name__ == ‘__main__‘:
l=[]print(os.cpu_count()) #本機為6核
start=time.time()for i in range(6):#p=Process(target=work) #耗時 4.732933044433594
p=Thread(target=work) #耗時 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()) #本機為6核
start=time.time()for i in range(40):#p=Process(target=work) #耗時9.001083612442017s多,大部分時間耗費在創建進程上
p=Thread(target=work) #耗時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:
死鎖狀態:也就是不同線程都去等待對方去釋放鎖的情況(互相僵持)
建議:輕易不要去嘗試處理鎖的問題。
from threading importThread,Lock,current_thread,RLockimporttime"""Rlock可以被第一個搶到鎖的人連續的acquire和release
每acquire一次鎖身上的計數加1
每release一次鎖身上的計數減1
只要鎖的計數不為0 其他人都不能搶"""
#mutexA = Lock()#mutexB = Lock()
mutexA = mutexB = RLock() #A B現在是同一把鎖
classMyThread(Thread):def run(self): #創建線程自動觸發run方法 run方法內調用func1 func2相當于也是自動觸發
self.func1()
self.func2()deffunc1(self):
mutexA.acquire()print(‘%s搶到了A鎖‘%self.name) #self.name等價于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
信號量:
過程:
先有五個線程同時獲得鎖,然后運行完因為不同線程睡眠時間不同所以釋放時間也會不同,
之后先釋放出來的鎖便可以讓其他線程再去搶奪。
#信號量可能在不同的領域中 對應不同的知識點
"""互斥鎖:一個廁所(一個坑位)
信號量:公共廁所(多個坑位)"""
from threading importSemaphore,Threadimporttimeimportrandom
sm= Semaphore(5) #造了一個含有五個的坑位的公共廁所
deftask(name):
sm.acquire()print(‘%s占了一個坑位‘%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#先生成一個event對象
e =Event()deflight():print(‘紅燈正亮著‘)
time.sleep(3)
e.set()#發信號
print(‘綠燈亮了‘)defcar(name):print(‘%s正在等紅燈‘%name)
e.wait()#等待信號
print(‘%s加油門飆車了‘%name)
t= Thread(target=light)
t.start()for i in range(10):
t= Thread(target=car,args=(‘傘兵%s‘%i,))
t.start()"""先啟一個線程,打印路燈亮了,進入睡眠的同時,起了十個線程,之后打印正在等綠燈,
之后等待e.wait()收到信號,當運行e.set()發信號時,便可以繼續運行了。"""
View Code
線程queue:
importqueue"""同一個進程下的多個線程本來就是數據共享 為什么還要用隊列
因為隊列是管道+鎖 使用隊列你就不需要自己手動操作鎖的問題
因為鎖操作的不好極容易產生死鎖現象"""
#q = queue.Queue()#q.put(‘hahha‘)#print(q.get())
#q = queue.LifoQueue()#后進先出#q.put(1)#q.put(2)#q.put(3)#print(q.get())
#q = queue.PriorityQueue()## 數字越小 優先級越高#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
總結
以上是生活随笔為你收集整理的并发服务器的信号传递,使服务器支持并发、GIL全局解释器锁、死锁和Rlock、信号量、event事件、...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机学习的基本形式,电脑基础知识学习方
- 下一篇: dns的服务器地址是多少当前位置,dns