1.使用类创建子线程demo
import time
import threading
class Task(threading.Thread):
def run(self):
while True:
print('11111')
print(self.name)
time.sleep(1)
t=Task()
t.start()
while True:
print('main')
time.sleep(1)
小结
1.可以自己定义一个类,但是这个类要继承Thread
2.一定要实现run方法,即要定义一个run方法,并且实现线程需要执行的代码
3.当调用自己编写的类创建出来的实例对象的run方法,会创建新的线程,并且线程会自动调用run方法
4.如果出了run方法之外还定义了很多其他的方法,例如xx,那么这些方法需要在run方法中自己去调用线程它不会自动调用
2.并发TCP服务器
import socket
import threading
class HandleData(threading.Thread):
def __init__(self,client_socket):
super().__init__()
self.client_socket=client_socket
def run(self):
#5.接收/发送数据
while True:
recv_content=self.client_socket.recv(1024)
if len(recv_content) !=0:
print(recv_content)
self.client_socket.send(recv_content)
else:
self.client_socket.close()
break
class TCPServer(threading.Thread):
def __init__(self,port):
super.__init__()
self.server_s=None
#1.创建套接字
self.server_s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2.绑定本地信息
self.seerver_s.bind(('',port))
#3.将套接字由默认的主动链接模式改为被动模式(监听模块)
self.server_s.listen(128)
def run(self):
while True:
#4.等待客户端进行链接
new_s,client_info=self.server_s.accept()
print(client_info)
#创建一个新的线程,专门为刚刚链接的客户端服务
handle_data_thread=HandleData(new_s)
handle_data_thread.start()
def __del__(self):
#6.关闭套接字
self.server_s.close()
tcp_seerver=TCPServer(7788)
tcp_server.start()
3.队列
3.1队列Queue
import queue
q=queue.Queue()
q.put('11') #存入字符串
q.put(22) #存入整数
q.put({'num':100}) #存入字典
print(q.get())
print(q.get())
print(q.get())
1.FIFO(first in first out先进先出)
2.可以存放任意类型数据,例如整数、字符串、字典
3.使用put放数据
4.使用get取数据(如果当前队列中没有数据,此时会堵塞)
3.2堆栈Queue
import queue
q=queue.LifoQueue()
q.put('11')
q.put(22)
q.put({'num':100})
print(q.get())
print(q.get())
print(q.get())
1.LIFO(last in first out后进先出)
2.可以存放任意类型数据,例如整数、字符串、字典
3.使用put放数据
4.使用get取数据(如果当前队列中没有数据,此时会堵塞)
3.3 优先级Queue
import queue
q=queue.PriorityQueue()
q.put((10,'Q'))
q.put((30,'Z'))
q.put((20,'A'))
print(q.get())
print(q.get())
print(q.get())
1.根据优先级来确定当前要获取的数据
2.使用put存放
1.将一个元组放到里面
2.第1个元素:优先级,数字越小优先级越高
3.第2个元素是:要存放的数据
3.使用get来获取(如果当前队列中没有数据,此时会堵塞)
4.带有聊天记录的udp聊天程序
import threading
import socket
import queue
class RecvMsg(threading.Thread):
def __init__(self,udp_s):
super.__init__()
self.udp_s=udp_s
def run(self):
while True:
recv_content,client_info=self.udp_s.recvfrom(1024)
temp_content='>>>%s(%d):%s'%(client_info[0],client_info[1],recv_content.decode('gbk'))
print(temp_content)
self.q.put(temp_content)
def __del__(self):
self.upd_s.close()
class SendMsg(threading.Thread):
def __init__(self,udp_s):
super.__init__()
self.udp_s=udp_s
def run(self):
while True:
dest_ip=input('请输入对方的ip:')
dest_port=int(input('请输入对方的port:'))
while True:
send_content=input('请输入要发送的数据')
if send_content:
self.udp_s.sendto(send_content.encode('utf-8'),(dest_ip,dest_port))
temp_info='<<<%s(%d):%s'%(dest_ip,dest_port,send_content)
self.q.put(temp_info)
else:
break
def __del__(self):
self.upd_s.close()
class ChatHistory(threading.Thread):
def __init__(self,q):
super().__init__()
self.q=q
def run(self):
while True:
#从Queue中读取数据
content=self.q.get()
#将数据写入到文件
with open('./chat.txt','a') as f:
f.write()
f.write('\n')
def main():
#1.创建一个udp套接字
udp_s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#2.绑定本地信息
udp_s.bind('',7890)
#创建一个FIFO的队列
q=queue.Queue()
#3.创建一个新的线程对象-目的用来接收数据
recv_msg_thread=RecvMsg(udp_s,q)
#4.创建一个新的线程对象-目的用来检测键盘发送数据
send_msg_thread=SendMsg(udp_s,q)
#5.创建一个新的线程对象-目的用来存储聊天记录
chat_history_thread=ChatHistory(q)
recv_msg_thread.start()
send_msg_thread.start()
caht_history_thread.start()
if __name__='__main__':
main()
5.多线程-共享全局变量
import threading
import time
#1.定义一个全局变量
g_num=0
#2.定义2个函数-用他们来充当线程要执行的代码
def task1():
global g_num
g_num=100
print('task1中,g_num=%d'%g_num)
def task2():
print('task2中,g_num=%d'%g_num)
#3.创建线程对象
t1=threading.Thread(target=task1)
t2=threading.Thread(target=task2)
#4.调用start创建线程,让线程开始运行
t1.start()
time.sleep(2) #让主线程延迟一会,保证让task1这个任务先执行完毕
t2.start()
小结
1.如果一个线程有多个线程,每个线程可以单独执行自己的任务
2.如果多个线程直接需要数据共享,最简单的方式是,通过全局变量来实现
1.一个线程修改全局变量
2.另外一个线程从全局变量中获取数据
通过全局变量可以解决多个线程之间共享数据的问题,但是,如果使用不够恰当,会导致数据错乱
例如有3个线程:
其中第1、2个修改全局变量,第3个线程获取全局变量的值,可能会出现第1个线程刚刚将数据存放到了全局变量中,本意是想让第3个线程获取它的数据,因为操作系统的调度原因导致了线程3没有被调度,而第2个线程被调度了,恰巧第2个线程也对全局变量进行了修改。当第3个线程去读取数据的时候,读取到的是第2个线程的数据而不是在第1个线程的数据
6.资源竞争的问题
import threading
import time
#1.定义一个全局变量
g_num=0
#2.定义2个函数-用他们来充当线程要执行的代码
def task1(num):
global g_num
for i in range(num):
g_num+=1
print('task1中,g_num=%d'%g_num)
def task2(num):
global g_num
for i in range(num):
g_num+=1
print('task2中,g_num=%d'%g_num)
#3.创建线程对象
t1=threading.Thread(target=task1,args=(100,))
t2=threading.Thread(target=task2,args=(100,))
#4.调用start创建线程,让线程开始运行
t1.start()
#time.sleep(2) #让主线程延迟一会,保证让task1这个任务先执行完毕
t2.start()
g_num+=1应该是一个整体的功能即一气呵成,但是操作系统在执行这个本应该一次性全部执行的代码时,刚执行了一部分,反而去执行其他线程的代码,导致了这样的问题产生
7.互斥锁
import threading
#创建一个互斥锁
mutex=threading.Lock()
#上锁
mutex.acquire()
print('哈哈哈')
mutex.acquire()
#解锁
mutex.release()
如果想要对某些代码一起执行,不想被其他的线程打扰,可以将这些代码放到互斥锁上锁到互斥锁解锁之间
如果这个互斥锁已经被上锁了,那么在这个锁被解开之前,是不能再次上锁的,也就是说:如果这个锁被上锁在解开之前谁要是再次调用acquire对其上锁,那么谁就会被堵塞,直到这个互斥锁被解锁为止
注意:一定是同一把锁才可以