本章節將介紹主線程與子線程的關系;使用udp利用多線程在python環境下實現全雙工通信代碼的三種實現;查看當前所有線程;資源競爭;互斥鎖;死鎖。
一、主線程與子線程的關系:
1,若主線程無代碼執行,主線程將等待子線程結束而結束。
2,線程的運行並無先后順序。
3,若主線程因特殊原因先結束,子線程也同時結束。
4,只有當用Thread創建出來的實例對象,被start方法調用時,才會創建線程並運行。
5,多線程間共享全局變量,但若多線程同時對同一全局變量操作,容易造成資源競爭,可以使用互斥鎖解決,但要避免形成死鎖。
二、實現全雙工通信代碼如下:
1,函數定義實現:
import socket
import threading
def recv_msg(udp_socket):
"""接收數據並顯示"""
# 接收數據
while True:
recv_data = udp_socket.recvfrom(1024)
print(recv_data)
def send_msg(udp_socket, dest_ip, dest_port):
"""發送數據"""
# 發送數據
while True:
send_data = input("輸入要發送的數據:")
udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port))
def main():
"""完成udp聊天器的整體控制"""
# 1. 創建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 綁定本地信息
udp_socket.bind(("", 7890))
# 3. 獲取對方的ip
dest_ip = input("請輸入對方的ip:")
dest_port = int(input("請輸入對方的port:"))
# 4. 創建2個線程,去執行相應的功能
t_recv = threading.Thread(target=recv_msg, args=(udp_socket,))
t_send = threading.Thread(target=send_msg, args=(udp_socket, dest_ip, dest_port))
t_recv.start()
t_send.start()
if __name__ == "__main__":
main()
2,自定義類對象實現:
import socket
#導入多線程模塊
import threading
#定義udp通信類對象
class UDP_TALKER(object):
def __init__(self):
self.udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
self.udp_socket.bind(('',8880))
def send(self):
while True :
message = input('請輸入發送消息:')
self.udp_socket.sendto(message.encode('utf-8'),('',8881))
def recv(self):
while True :
info = self.udp_socket.recvfrom(1024)
print(info[0].decode('utf-8'))
def run_forever(self):
# threading.Thread(target = 函數名,args = (元組參數))
#返回實例對象,創建線程
t1 = threading.Thread(target = self.send)
t2 = threading.Thread(target = self.recv)
#調用線程並運行
t1.start()
t2.start()
def main():
udp_talker = UDP_TALKER()
udp_talker.run_forever()
if __name__ == '__main__':
main()
3,繼承線程類對象實現:
#針對於一個線程對應多個函數。
import socket
import threading
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udp_socket.bind(('',8881))
class Mythreadsend(threading.Thread):
def run(self):
while True :
self.send()
def send(self):
message = input('請輸入消息:')
udp_socket.sendto(message.encode('utf-8'),('',8880))
class Mythreadrecv(threading.Thread):
def run(self):
while True :
self.recv()
def recv(self):
info,addr = udp_socket.recvfrom(1024)
print(info.decode('utf-8'))
if __name__ == '__main__':
t1 = Mythreadsend()
t2 = Mythreadrecv()
t1.start() #自動調用run()函數
t2.start() #自動調用run()函數
三,查看線程:
#查看當前所有線程,並返回列表。
threading.enumerate()
四,資源競爭:
python語句在執行的過程中可能會解析多條語句執行,並非一次執行完畢,所以在多線程執行同一全局變量時可能會出現計算結果重復執行的情況,可以只用互斥鎖進行解決。
如果多線程中,共享全局變量,而且同一時刻都在對全局變量進行操作,可能會出現問題。可以使用互斥鎖解決,但要避免出現死鎖。
五,互斥鎖:
某個線程要更改共享數據時,先將其鎖定,此時資源的狀態為“鎖定”,其他線程不能更改;直到該線程釋放資源,將資源的狀態變成“非鎖定”,其他的線程才能再次鎖定該資源。互斥鎖保證了每次只有一個線程進行寫入操作,從而保證了多線程情況下數據的正確性。
#創建一個互斥鎖,默認是沒有上鎖的
mutex = threading.Lock()
#上鎖。如果之前沒有上鎖,上鎖成功,如果之前已經上鎖,會阻塞在這里,直到鎖被解開。
mutex.acquire()
#解鎖
mutex.release()
六,死鎖:
使用互斥鎖的時候需要注意死鎖的問題,要在合適的地方注意釋放鎖。
死鎖一旦發生就會造成應用的停止響應。
