目录
threading模块中除_thread模块方法外的其他方法
threading模块中除_thread模块方法外的其他方法
方法 | 描述 |
threading.currentThread() | 返回当前的线程变量 |
threading.enumerate() | 返回一个包含正在运行的线程的列表 |
threading.activeCount() | 返回正在运行的线程数量,结果同len(threading.enumerate()) |
Thread类常用实例化参数
参数 | 参数说明 |
target | 要调用执行的入口函数 |
name | 为线程命名 |
args和kwargs | 入口函数的参数 |
daemon | 作用同setDaemon()方法,值为True时作为守护线程(后台线程) |
Thread类实例化方法
方法 | 描述说明 |
run() | 表示线程活动的方法 |
start() | 启动线程活动 |
join(timeout=None) | 阻塞调用线程,用来结束子线程,至线程的join() 方法被调用终止(或正常退出,或抛出未处理的异常,或指定的超时时间)后继续执行后面的代码 |
isAlive() | 返回线程是否活动的 |
getName() | 返回线程名 |
setName() | 设置线程名 |
setDaemon() | 守护线程(后台线程),和join()方法相反,主线程执行完后结束线程,不会等待子线程是否结束。设置在start()启动线程之前。 |
三种形式的进程执行
- 主线程和子线程都执行完成后结束代码执行,执行的顺序取决于线程的实际运行时间,最终不管是主线程还是子线程都会执行完成。要求在创建和执行线程时不要设置join阻塞和守护线程(后台线程)。
- 子线程执行完成后再执行主线程,子线程之间执行不会互相影响。这就需要对子线程设置join阻塞等待。
- 守护模式时,只要主线程执行完后结束所有子线程执行,不会等待子线程是否结束。当同时设置了守护模式和阻塞等待的时候,优先生效的是阻塞等待设置。
创建线程
通过Thread类直接创建线程对象
from threading import Thread
import time
# 定义一个入口函数
dic = {'王小二':2900,'张大毛':1001,'马中立':1999}
def func(name,amount,times):
print('我叫%s,我要存款%s元'%(name,amount))
print('业务办理中,请稍等......')
time.sleep(times)
print('客户%s您好,您存入了%s元,账户余额%s元'%(name,amount,amount+dic[name]))
# 创建线程
th1 = Thread(name='用户001',target=func,args=('王小二',3000,8),daemon=True)
th2 = Thread(name='用户002',target=func,args=('张大毛',2500,4),daemon=True)
# 创建守护线程(后台线程),同参数daemon=True的使用,二选一使用
# th1.setDaemon(True);th2.setDaemon(True)
# 开启线程
th1.start();th2.start()
# 设置阻塞等待,可以添加阻塞等到的超时时间,超过超时时间后结束线程
th1.join();th2.join(timeout=3)
# 主线程执行代码
print(th1.getName());print(th2.getName()) # 获取线程名
print('\n业务结束!')
通过继承Thread类自定义线程类创建线程
from threading import Thread
import time
class MyThread(Thread):
dic = {'王小二': 2900, '张大毛': 1001, '马中立': 1999}
def __init__(self,name,amount,times):
super().__init__() # 重用父类方法'
self.name = name
self.amount = amount
self.times = times
def run(self):
print('我叫%s,我要存款%s元' % (self.name, self.amount))
print('业务办理中,请稍等......')
time.sleep(self.times)
print('客户%s您好,您存入了%s元,账户余额%s元'%(self.name,self.amount,self.amount+self.dic[self.name]))
Th1 = MyThread('王小二',3000,8)
Th2 = MyThread('张大毛',2500,4)
Th1.start();Th2.start()
Th1.join();Th2.join()
print(Th1.getName());print(Th2.getName()) # 获取线程名
print('\n业务结束!')
共享数据访问控制(多线程之间的通信)
在多线程开发中经常遇到多个线程里面的代码需要访问同一个公共数据对象时(这个公共的数据对象可是任何类型,如列表、字典、函数或自定义类等),程序需要防止线程的代码同时操作公共数据对象时可能导致的数据访问互相冲突影响。这时需要线程锁来保护数据不受彼此的影响。
保护数据区间加解锁
如下面的代码,假设一个入款程序中有多个入口同时入款100000元,但存款只能一次1元存入,正确结果为310000,在没有线程锁保护的情况下,实际结果并非如此。
from threading import Thread,Lock
lock = Lock() # 创建一个锁工具
Fund_Amount = 10000
def addFund(amount):
global Fund_Amount
for i in range(amount):
lock.acquire() # 数据加锁
Fund_Amount = Fund_Amount + 1
lock.release() # 数据解锁
th1 = Thread(target=addFund,args=(100000,),daemon=True)
th2 = Thread(target=addFund,args=(100000,),daemon=True)
th3 = Thread(target=addFund,args=(100000,),daemon=True)
th1.start();th2.start();th3.start()
th1.join();th2.join();th3.join()
print('汇款总金额为%s元'%Fund_Amount)
注释线程锁的结果:执行多次,每次的结果都不相同,也不等于310000
加了锁的结果 :
上下文管理
添加线程锁有两种不同的方法,除上面一种在数据保护区间中使用加锁和解锁的方法外,还可以使用上下文管理的方法,将要保护的数据添加进去,如下:
from threading import Thread,Lock
lock = Lock() # 创建一个锁工具
Fund_Amount = 10000
def addFund(amount):
global Fund_Amount
for i in range(amount):
with lock: # 使用上下文管理的方法添加线程锁
Fund_Amount = Fund_Amount + 1
th1 = Thread(target=addFund,args=(100000,),daemon=True)
th2 = Thread(target=addFund,args=(100000,),daemon=True)
th3 = Thread(target=addFund,args=(100000,),daemon=True)
th = []
th.append(th1);th.append(th2);th.append(th3)
for t in th:
t.start()
t.join()
print('汇款总金额为%s元'%Fund_Amount)
其结果同加解锁:
队列
python的queue模块中提供了队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。
参考:https://www.runoob.com/python3/python3-multithreading.html
Queue队列方法
队列方法 | 功能 |
qsize() | 返回队列的大小 |
empty() | 如果队列为空,返回True,反之False |
full() | 如果队列满了,返回True,反之False,Queue.full 与 Queue.maxsize 大小对应 |
get([block[, timeout]]) | 获取队列,timeout等待时间 |
get_nowait() | 相当Queue.get(False) |
put() | 写入队列,可设置timeout等待时间 |
task_done() | 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号 |
join() | 等到队列为空,再执行别的操作 |
线程池
from multiprocessing.pool import ThreadPool # 导入线程池模块
import time
# 创建线程池,括号中可以指定创建的线程池的数量
pool = ThreadPool(2)
# 创建函数作为作为的执行函数
def func1(name,age,times):
print(f'我叫{name},今年{age}岁')
time.sleep(times)
print('我的回答完毕!')
def func2(times,name='小明',grade=90):
print('我是%s,我的成绩是%s'%(name,grade))
time.sleep(times)
print('我的回答完毕!')
# 创建线程实例并执行
pool.apply_async(func1,args=('小马',17,5))
pool.apply_async(func2,args=(3,),kwds={'name':'小虎','grade':88})
pool.apply_async(func2,args=(3,))
pool.apply_async(func1,args=('小龙',15,3))
# close方法用来关闭提交通道,不允许再提交任务
pool.close()
# 阻塞等待
pool.join()
#主线程代码
print('几位同学介绍完毕。')