本章节将介绍主线程与子线程的关系;使用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()
六,死锁:
使用互斥锁的时候需要注意死锁的问题,要在合适的地方注意释放锁。
死锁一旦发生就会造成应用的停止响应。