[TOC]
一、python线程模块的选择
Python提供了几个用于多线程编程的模块, 包括thread、threading和Queue等 。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作, 至少threading模块能确保重要的子线程退出后进程才退出 。
thread模块不支持守护线程 ,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。
二、threading模块
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍(官方链接)
三、开启子线程的两种方式
线程的开启和进程不同,进程是拷贝一份代码去内存中执行。内部调用的是fork创建子进程
而线程是会去执行的指定函数
- 通过指定函数的方式
- 通过类的继承,实现run方法
方式1:
# 方式一
def task(name):
print(f'{name} start')
global x
x -= 1
print({name}, x)
time.sleep(2)
print(f'{name} end')
if __name__ == '__main__':
x = 10
t1 = Thread(target=task,args=("线程1",))
t2 = Thread(target=task,args=("线程2",))
t1.start() # 告诉操作系统开一个线程
t2.start() # 告诉操作系统开一个线程
print('主')
线程1 start {'线程1'} 9 线程2 start {'线程2'} 8 主 线程1 end 线程2 end
方式2
# 方式二
class Mythread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
print(f'{self.name} start')
global x
x -= 1
print({self.name}, x)
time.sleep(2)
print(f'{self.name} end')
if __name__ == '__main__':
x = 10
t1 = Mythread("线程1")
t2 = Mythread("线程2")
t1.start()
t2.start()
print('主')
线程1 start {'线程1'} 9 线程2 start {'线程2'} 8 主 线程1 end 线程2 end
通过上面两组代码你会发现:线程的创建运行比进程快,同一个进程中的线程可以共享资源
四、join回收子线程
和进程一样,等待所以子线程结束然后去回收
from threading import Thread
import time
'''
等待回收子线程
'''
def task():
print('子线程 start')
time.sleep(2)
print('子线程 end')
t = Thread(target=task)
t.start()
t.join() # 等待子线程运行结束
print('主线程')
五、Thread类的其他方法
Thread实例对象的方法:
-
isAlive()
:返回线程是否活动的。 -
getName()
:返回线程名。 -
setName()
:设置线程名。
threading模块提供的一些方法:
-
threading.currentThread()
:返回当前的线程变量。 -
threading.enumerate()
:返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 -
threading.activeCount()
:返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread,currentThread,enumerate,activeCount
import time
'''
线程的其他用法:
1.查看是否存在 is_alive() # True or False
2.获取线程名 getName()
3.设置线程名 setName(name)
4.当前线程 currentThread() 需要从threading导入currentThread模块
5.活动线程总和 activeCount() 需要从threading导入activeCount模块
6.查看所有线程 enumerate() 需要从threading导入enumerate模块
'''
def task():
print('子线程 start')
time.sleep(2)
print('子线程 end')
if __name__ == '__main__':
t1 = Thread(target=task)
t2 = Thread(target=task)
t1.start()
t2.start()
print("查看线程状态,设置线程名,获取线程名","-" * 50)
print(t1.is_alive()) # 查看是否存在
print(t2.is_alive()) # 查看是否存在
t1.setName("子线程一") # 设置线程1名字
t2.setName("子线程二") # 设置线程2名字
print(t1.getName()) # 获取线程名
print(t2.getName()) # 获取线程名
print("查看当前线程","-" * 50)
print(currentThread()) # 查看当前线程
print(currentThread().name) # 查看当前线程名
print("查看所有线程","-"*50)
print(activeCount()) # 查看存活线程个数
print(enumerate()) # 查看所有线程
print(len(enumerate())) # 查看所有线程个数
六、基于多线程实现socket多用户间通讯
客户端
import socket
from threading import Thread
def task(conn,client_addr):
# 通讯循环
while 1:
try:
msg = conn.recv(1024)
if not msg:
break
print(client_addr,"消息:",msg.decode("utf8"))
msg = input("请输入内容")
conn.send(msg.encode("utf8"))
except Exception as e:
print(e)
break
if __name__ == '__main__':
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",8080))
server.listen(5)
# 链接循环
while 1:
print("等待连接...")
conn, addr = server.accept()
print(addr,"连接成功")
t = Thread(target=task,args=(conn,addr))
t.start()
客户端
import socket
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",8080))
while 1:
msg = input("请输入内容")
client.send(msg.encode("utf8"))
data = client.recv(1024)
if not data:
break
print(data.decode("utf8"))