一、理论基础
单线程:
独立的任务挨个运行多线程:
独立的任务同时运行。底层是将一个大任务分多线程,线程流水线式运行
举例:假定有两个循环函数。一个需10秒运行结束,另一个需12秒运行结束单线程:
挨个运行,共需22秒运行结束多线程:
同时运行,共需12秒运行结束(以时间长的子线程运行时间为准)不使用锁:
主线程自定义等待时间(足够所有子线程运行结束),等待时间结束后主线程继续运行
-----注意!如果不自定义等待时间,则可能主线程运行结束了子线程还没运行完使用锁:
主线程不用自定义时间,子线程运行结束后释放锁,主线程立即继续运行不使用守护线程:
主线程若退出,则子线程无论是否运行结束都会强制退出使用守护线程:
主线程若退出,受守护的子线程不会退出,不受守护的子线程强制退出
使用模块及语法:
import thread #不推荐使用,适合高级底层专家使用
import threading #更高级、更全面。推荐使用
import Queue #可以创建队列数据结构,使其在多线程之间共享
threading模块中的对象(类):
方法 | 解释 |
Thread | 表示一个执行线程的对象 |
Lock | 锁原语对象 |
RLock | 可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁) |
Condition | 条件变量对象,使得一个线程等待另一个线程满足特定的“条件” |
Event | 条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有线程将被激活 |
Semaphore | 为线程间共享的有限资源提供了一个“计数器”,如果没有可用资源时会被阻塞 |
BoundedSemaphore | 与Semaphore相似,不过它不允许超过初始值 |
Timer | 与Thread相似,不过它要在运行前等待一段时间 |
threading模块中thread类的对象和方法:
Thread对象数据属性
方法 | 解释 |
name | 线程名 |
ident | 线程的标识符 |
daemon | 布尔标志,表示这个线程是否是守护线程 |
Thread对象方法
方法 | 解释 |
init() | 实例化一个线程对象。注意是单下划线 |
start() | 开始执行该线程 |
run() | 定义线程功能的方法(通常在子类中被应用开发者重写) |
join(timeout=None) | 直至启动的线程终止之前一直挂起;除非赋予等待值,否则会一直阻塞 |
threading模块中的其它函数:
方法 | 解释 |
enumerate() | 返回当前活动的Thread对象列表 |
settrace(func) | 为所有线程设置一个trace函数 |
setprofile(func) | 为所有线程设置一个profile函数 |
stack_size(size=0) | 返回新创建线程的栈大小;或为后续创建的线程设定栈的大小为size |
Queue模块的常用属性:
Queue模块的类
方法 | 解释 |
Queue(最大值) | 创建一个先入先出队列。如果给定最大值,则在队列没有空间时阻塞;否则为无限队列 |
LifoQueue(最大值) | 创建一个后入先出队列。如果给定最大值,则在队列没有空间时阻塞;否则为无限队列 |
PriorityQueue(最大值) | 创建一个优先级队列。如果给定最大值,则在队列没有空间时阻塞,否则为无限队列 |
Queue异常
方法 | 解释 |
Empty | 当对空队列调用get*()方法时抛出异常 |
Full | 当对已满的队列调用put*()方法时抛出异常 |
Queue对象方法
方法 | 解释 |
qsize() | 返回队列大小(该值为近似值) |
empty() | 如果队列为空,则返回 True;否则,返回 False |
full() | 如果队列已满,则返回 True;否则,返回 False |
put(item,block,timeout) | 将item 放入队列。若block为 True且timeout为None,则在有可用空间之前阻塞;如果timeou为正值,则最多阻塞 timeout秒;如果block为False,则抛出Empty异常 |
put_nowait(item) | 和 put(item,False)相同 |
get(block,timeout) | 从队列中取得元素。如果给定了block,则一直阻塞到有可用的元素为止 |
get_nowait() | 和 get(False)相同 |
task_done() | 用于表示队列中的某个元素已执行完成,该方法会被下面的join()使用 |
join() | 在队列中所有元素执行完毕并调用上面的task_done()信号之前,保持阻塞 |
多线程编程相关模块:
方法 | 解释 |
thread | 基本的、低级别的线程模块 |
threading | 高级别的线程和同步对象 |
multiprocessing | 使用“threading”接口派生/使用子进程 |
subprocess | 完全跳过线程,使用进程来执行 |
Queue | 供多线程使用的同步先入先出队列 |
mutex | 互斥对象 |
SocketServer | 创建/管理线程控制的TCP/UDP服务器 |
串讲—Python程序的执行顺序:
以最外面的代码由上至下执行,函数/类代码没有顺序,只有在调用他们的时候执行。
二、使用Threading模块创建线程
代码实例:
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import threading #导入模块
from time import sleep,ctime
loops=(12,8) #变量赋值。分别是12秒和8秒
class mythread(threading.Thread): #定义类。继承父类
def __init__(self,func,args,name=''): #定义类内的初始函数(__init__函数),用于接收传参
threading.Thread.__init__(self) #父类(Thread)中的初始函数的声明
self.name=name #将函数参数转换为类内全局变量
self.func=func
self.args=args
def run(self): #定义表示线程活动的函数
self.func(*self.args) #全局声明
def loop(nloop,nsec): #定义函数。有两个变量(括号里致使变量名而已)
print ("\n循环%s,开始时间为:%s" % (nloop,ctime()))
sleep(nsec)
print ("循环",nloop,"完成时间:",ctime())
def main(): #定义主函数
print ("开始:",ctime()) #开始时间。即时取时
threads=[] #定义了一个空列表。列表名为threads
nloops=range(len(loops)) #len(loops)=2,range(len(loops))=[0,1]。即两个列表值
for i in nloops: #第一个循环
t = mythread(loop,(i,loops[i]),loop.__name__) #定义实参了
threads.append(t) #整个类加进来了
for i in nloops: #第二个循环
threads[i].start() #启动线程活动
for m in nloops: #第三个循环
threads[m].join() #等待线程终止
print ("都已完成:",ctime())
if __name__=="__main__": #独立运行脚本的惯用方法,判断函数是否为主程序
main()
-----------------------------------------------------------------
输出结果如下:
开始: Mon Mar 30 17:19:25 2020
循环0,开始时间为:Mon Mar 30 17:19:25 2020
循环1,开始时间为:Mon Mar 30 17:19:25 2020
循环 1 完成时间: Mon Mar 30 17:19:33 2020
循环 0 完成时间: Mon Mar 30 17:19:37 2020
都已完成: Mon Mar 30 17:19:37 2020
代码实例:
#! /usr/bin/python
# -*- coding: UTF-8 -*-
import threading
import time
exitFlag = 0
class myThread (threading.Thread): #继承父类threading.Thread
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self): #把要执行的代码写到run函数里面线程在创建后会直接运行run函数
print ("开始" + self.name)
print_time(self.name, self.counter,5) #调用函数。同时对函数传入实参
print ("退出" + self.name)
def print_time(threadName, delay, counter): #定义函数。有三个形参(括号里只是变量名而已),接收实参传入
while counter:
if exitFlag: #if语句,和代码头处的想对应
threading.Thread.exit() #退出
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1 #counter=counter-1
thread1 = myThread(1, "xiancheng-1", 1) #创建新线程。类的实例化,传递三个参数给类,类接收
thread2 = myThread(2, "xiancheng-2", 2)
thread1.start() #开启线程
thread2.start()
print ("退出主线程")
-----------------------------------------------------------------
输出结果如下:
开始xiancheng-1
开始xiancheng-2
退出主线程
xiancheng-1: Mon Mar 30 17:24:11 2020
xiancheng-2: Mon Mar 30 17:24:12 2020
xiancheng-1: Mon Mar 30 17:24:12 2020
xiancheng-1: Mon Mar 30 17:24:13 2020
xiancheng-2: Mon Mar 30 17:24:14 2020
xiancheng-1: Mon Mar 30 17:24:14 2020
xiancheng-1: Mon Mar 30 17:24:15 2020
退出xiancheng-1
xiancheng-2: Mon Mar 30 17:24:16 2020
xiancheng-2: Mon Mar 30 17:24:18 2020
xiancheng-2: Mon Mar 30 17:24:20 2020
退出xiancheng-2