创建多线程
一、python线程模块的选择
Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出。
thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍(官方链接)
二、通过threading.Thread类创建线程
2.1创建线程方式一
from threading import Thread
import time
def task():
print("线程 stats")
time.sleep(4)
print("线程 end")
t = Thread(target=task)
t.start() # 告诉操作系统开启一个线程
print("程序入口")
线程 stats
程序入口
线程 end
2.2 创建线程方式二
from threading import Thread
import time
class MyThread(Thread):
def run(self):
print("子进程 stat")
time.sleep(3)
print("子进程 end")
if __name__ == '__main__':
t = MyThread()
t.start() # 告诉操作系统开启一个线程
print("主进程")
子进程 stat
主进程
子进程 end
三、多线程与多进程
3.1 pid比较
from threading import Thread
from multiprocessing import Process
import os
def work():
print('hello',os.getpid())
if __name__ == '__main__':
# part1:在主进程下开启多个子线程,每个线程都跟主进程的pid一样
t1=Thread(target=work)
t2=Thread(target=work)
t1.start()
t2.start()
print('主线程/主进程pid',os.getpid())
# part2:开多个子进程,每个进程都有不同的pid,主pid号小于子进程的pid号
p1=Process(target=work)
p2=Process(target=work)
p1.start()
p2.start()
print('主线程/主进程pid',os.getpid())
'''
3.2 开发效率比较
from threading import Thread
from multiprocessing import Process
import os
def work():
print('hello')
if __name__ == '__main__':
# 在主进程下开启线程
t=Thread(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
hello
主线程/主进程
'''
# 在主进程下开启子进程
t=Process(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
主线程/主进程
hello
'''
""""
开启子进程的速度大于开启子线程的速度
主要原因,开启进程需要申请内存空间,所以比较慢
而开启子线程只是告诉操作系统一个执行方案,快
"""
3.3 内存数据的共享问题
from threading import Thread
import time,os
x = 10
def task():
print("开启子线程")
global x
x = 399
time.sleep(3)
print(f"子线程中的x={x}")
print("子线程结束")
print("子线程中的id:", os.getpid())
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print(x)
print("主进程中的pid", os.getpid())
""""
总结:
创建一个项目中子线程和主进程中id号相同
并且子进程共享主进程中的资源
"""
开启子线程
399
主进程中的pid 19372
子线程中的x=399
子线程结束
子线程中的id: 19372
四、Thread类的其他方法
Thread实例对象的方法:
isAlive()
:返回线程是否活动的。getName()
:返回线程名。setName()
:设置线程名。
threading模块提供的一些方法:
threading.currentThread()
:返回当前的线程变量。threading.enumerate()
:返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。threading.activeCount()
:返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread
import threading
from multiprocessing import Process
import os
def work():
import time
time.sleep(3)
print(threading.current_thread().getName())
if __name__ == '__main__':
# 在主进程下开启线程
t=Thread(target=work)
t.start()
print(threading.current_thread().getName())
print(threading.current_thread()) # 主线程
print(threading.enumerate()) # 连同主线程在内有两个运行的线程
print(threading.active_count())
print('主线程/主进程')
'''
打印结果:
MainThread
<_MainThread(MainThread, started 140735268892672)>
[<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
主线程/主进程
Thread-1
'''
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' %name)
if __name__ == '__main__':
t=Thread(target=sayhi,args=('randy',))
t.start()
t.join()
print('主线程')
print(t.is_alive())
'''
randy say hello
主线程
False
'''
五、join方法
from threading import Thread
import time
def task():
print("子进程")
time.sleep(3)
print("子进程结束")
if __name__ == '__main__':
t = Thread(target=task)
t.start()
# join等待子进程后再执行join下面的代码
t.join()
print("主进程")
子进程
子进程结束
主进程
from threading import Thread
import time
def task(name, n):
print(f"子进程{name}")
time.sleep(n)
print(f"子进程{name}结束")
if __name__ == '__main__':
t1 = Thread(target=task, args=('线程1', 1))
t2 = Thread(target=task, args=('线程2', 2))
t3 = Thread(target=task, args=('线程3', 3))
start = time.time()
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
"""
t1.start()
t1.join()
t2.start()
t2.join()
t3.start()
t3.join()
end = time.time()
print(end-start)
6.00290060043335
"""
# 思考一下 在单核的情况下 多个线程是如何利用cpu的
end = time.time()
print(end-start)
子进程线程1
子进程线程2
子进程线程3
子进程线程1结束
子进程线程2结束
子进程线程3结束
3.0021369457244873
5.1 比较进程中的join
from multiprocessing import Process
from threading import Thread
import time
def task():
print('进程 开启')
time.sleep(10)
print('进程 结束')
def task2():
print('子线程 开启')
time.sleep(2)
print('子线程 结束')
if __name__ == '__main__':
p = Process(target=task)
t = Thread(target=task2)
p.start() # 开进程
t.start() # 开线程
# t.join()
print('子进程join开始')
p.join() # 主进程的主线程等待子进程运行结束?????????????
print('主')
子线程 开启
子进程join开始
进程 开启
子线程 结束
进程 结束
主
六、多线程实现socket
6.1 服务器
from threading import Thread
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(3)
def task_server(conn):
while True:
try:
data = conn.recv(1024).decode()
if not data: return
print(f'收到客户端:{data}')
# conn_send = input("请输入信息:")
# conn.send(conn_send.encode())
conn.send(data.upper().encode())
except Exception as e:
pass
if __name__ == '__main__':
print("等待连接....")
while True:
conn, add = server.accept()
print("连接成功!")
t = Thread(target=task_server, args=(conn,))
t.start()
6.2 客户端
import socket
clien = socket.socket()
clien.connect(('127.0.0.1', 8080))
while True:
inp_data = input("请输入消息:")
clien.send(inp_data.encode())
data = clien.recv(1024).decode()
print(f'收到服务器:{data}')