下面是多线程编程基础教程
python多线程编程
线程与进程
进程:进程就是执行中的应用程序,进程可派生新的进程来执行其他任务,各个进程通过进程间通信(IPC)进行信息共享
线程:与进程类似,但是线程实在同一个进程下执行的,并共享相通的上下文,就是类似在一个主进程下运行了多个迷你进程
线程包括开始,执行顺序,结束三部分组成,通过指令指针记录当前的上下文,当其他线程运行时,可以中断或睡眠,此种模式称为让步
一个进程中的各个线程与主线程共享同一片数据空间,线程一般通过并发方式执行
多个线程同时访问一块数据空间时,由于访问顺序不同会导致不同的结果,此种现象称为竞态条件
注意线程无法给予公平的执行时间
全局解释器锁
python代码由python的虚拟机控制,在解释器主循环中同时只能有一个控制线程执行,虽然内存中有许多程序,但在任意给定时刻只能有一个程序在运行,即在任意时刻只有一个线程被python解释器来执行
python虚拟机的访问由全局解释器锁(GIL)来控制,锁保证了只能有一个线程在运行
在多线程环境下,python解释器按照下列方式执行:
- 设置GIL
- 切换进一个线程中执行
- 执行下面的操作之一:
- 指定数量的字节码指令
- 线程主动让出控制权
- 把线程设置回睡眠状态(切换出线程)
- 解锁GIL
- 重复上述步骤
退出线程
当一个线程完成函数的执行时,就会退出,可以通过调用thread.exit()退出函数,或sys.exit()退出python的进程,还可以抛出异常
在pythn中使用线程
下面是一个单线程执行循环
from time import sleep,ctime def loop1(): print("loop1 Starting at {}".format(ctime())) sleep(4) print("loop1 Ending at {}".format(ctime())) def loop2(): print("loop2 Starting at {}".format(ctime())) sleep(2) print("loop2 Ending at {}".format(ctime())) def main(): print("Main starting at {}".format(ctime())) loop1() loop2() print("Main ending at {}".format(ctime())) if __name__ == '__main__': main() '''显示结果如下: Main starting at Fri May 22 08:21:08 2020 loop1 Starting at Fri May 22 08:21:08 2020 loop1 Ending at Fri May 22 08:21:12 2020 loop2 Starting at Fri May 22 08:21:12 2020 loop2 Ending at Fri May 22 08:21:14 2020 Main ending at Fri May 22 08:21:14 2020'''
- 有程序的执行看出用了6秒钟执行完
python中的threading模块
threading模块的对象
对象 描述 Thread 表示一个执行线程的对象 Lock 锁原语对象 RLock 可重入锁对象,使单一线程可以再次获得已持有的锁(递归锁) Condition 条件变量对象,使得一个线程等待另一个线程满足特定条件 Event 条件变量的通用版本,任意数量的线程等待某个事件发生,在事件发生后,所有的线程被激活 Semaphore 为线程间共享的有限资源提供一个计数器,若没有则会被阻塞 BoundedSemaphore 与Semaphore类似,但不允许超过初始值 Timer 与Thread类似,不过他要在运行前等待一段时间 Barrier 创建一个障碍,必须达到指定的数量的线程后才可以继续 python的threading模块支持守护线程,守护线程可以作为服务器线程,当主线程退出时,将其他的线程设置为守护线程时,程序不必在意守护线程是否终止
守护线程工作方式是:等待客户端请求若没有请求,守护线程为空闲状态
当主线程退出时,若不需要等待某下子线程的结束,可以子线程设置守护进程标记,thread.daemon=True
注意新的子线程会继承父线程的守护标记
整个python程序会在非守护线程结束后才终止程序的执行
threading的Thread类
属性 描述 Thread对象数据属性 ****** name 线程名 ident 线程的标识符 daemon 布尔标志,表示此线程是否为守护线程 Thread对象方法 ****** init(group=None,target=None,name=None,args=(),kwargs={},verbose=None,daemon=None) 实例化一个线程对象需要有一个可调用的target,以及其他参数args或kwargs,还可以传递name或group参数,verbose标志可以接收,而daemon的值会设定为thread.daemon属性/标志 start() 开始执行该线程 run() 定义线程功能的方法 join(timeout=None) 直至启动的线程终止前一直挂起,除非给出timeout,否则一直阻塞 getName() 返回线程名 setName(name) 设定线程名 isAlivel/is_alive() 布尔标志,表示线程是否还存活 isDaemon() 判断是否为守护线程 setDaemon(daemonic) 把线程的守护标志设定为布尔值daemonic(需要在线程start()之前调用)
- 需要注意set/getName方法已弃用,使用thread.name来获取或设定
- is/setDaemon已弃用,使用thread.daemon来获取或设定
创建线程方法
- 创建Thread的实例,传给他一个函数
- 创建Thread的实例,传给他一个可调用的实例(不做介绍)
- 派生Thread的子类,并创建子类的实例
创建Thread实例,传函数
import threading from time import ctime,sleep loopstime=[4,2] #定义每个线程需要等待的时间 def loop(nloop,nsec): #定义循环程序 print("loop {} started at {}".format(nloop,ctime())) sleep(nsec) print("loop {} ended at {}".format(nloop,ctime())) def main(): print("Main started at {}".format(ctime())) threads=[] #将实例化的对象存储在列表中,方便以后调用 nloops=range(len(loopstime)) #需要实例化线程的次数 for i in nloops: t=threading.Thread(target=loop,args=(i,loopstime[i])) #实例化线程对象 threads.append(t) #将创建的对象暂时存储在列表中 for i in nloops: threads[i].start() #开始执行线程 for i in nloops: threads[i].join() #等待所有线程执行完毕 print("All Done at {}".format(ctime())) if __name__=='__main__': main()
- 实例化每个Thread对象时,把函数(target)和参数(args)传进去,得到返回的Thead类实例对象
- 当所有的线程分配完成后,通过调用每个线程的start()方法执行线程,而不是在创建时就执行
- 注意对于join()函数可以不用调用,一旦线程start(),他们会一直执行,直到给定函数完成后退出,若主线程还有其余的事情时,不是等待线程完成,可以不调用join函数
派生Thread的子类,并创建子类的实例
from time import sleep,ctime import threading loops=(4,2) class MyThread(threading.Thread): def __init__(self,func,args,name=''): threading.Thread.__init__(self) self.func=func self.args=args self.name=name def run(self): return self.func(*self.args) def loop(nloop,nsec): print("loop {} started at {} ".format(nloop,ctime())) sleep(nsec) print("loop {} started at {} ".format(nloop,ctime())) def main(): print("Main started at {}".format(ctime())) threads=[] count=range(len(loops)) for i in count: t=MyThread(loop,(i,loops[i]),loop.__name__) threads.append(t) for i in count: threads[i].start() for i in count: threads[i].join() print("All done at {}".format(ctime())) if __name__ == '__main__': main() '''显示结果如下: Main started at Fri May 22 15:21:12 2020 loop 0 started at Fri May 22 15:21:12 2020 loop 1 started at Fri May 22 15:21:12 2020 loop 1 started at Fri May 22 15:21:14 2020 loop 0 started at Fri May 22 15:21:16 2020 All done at Fri May 22 15:21:16 2020 '''
threading模块的其他函数
函数 描述 activeCount/active_count() 当前活动的Thread对象个数 current Thread()/current_thread 返回当前的Thread对象 enumerate() 返回当前活动的Thread对象列表 setttace(func) 为所有线程设置一个trace函数 setprofile(func) 为所有线程设置一个profile函数 stack_size(size=0) 返回新创建线程的栈大小,或为后续创建的线程设定栈的大小为size
单线程与多线程的比较
from e import MyThread from time import sleep,ctime def fib(x): #斐波那契 sleep(0.3) if x < 2: return 1 return (fib(x-1) + fib(x-2)) def fac(x): #阶乘 sleep(0.4) if x < 2: return 1 return (x * fac(x-1)) def sum(x): #累加和 sleep(0.5) if x < 2: return 1 return (x + sum(x-1)) def main(): funcs=[fib,fac,sum] n=12 threads=[] count=range(len(funcs)) #Sigle Thread executed for i in count: print("{} Starting at {} ".format(funcs[i].__name__,ctime())) funcs[i](n) print("{} ended at {} ".format(funcs[i].__name__, ctime())) #————————————上述为单线程,下面为多线程—————————————————————— print("\n") print("MultiThread executed") def loop(nloop,nsec): print("loop {} started at {}".format(nloop,ctime())) sleep(nsec) print("loop {} ended at {}".format((nloop,ctime()))) for i in count: t=MyThread(funcs[i],(n,),funcs[i].__name__) threads.append(t) for i in count: threads[i].start() for i in count: threads[i].join() print(threads[i].getResult()) print("All done!!!") if __name__ == '__main__': main() '''显示结果如下: 单线程(用时2分30秒): fib Starting at Fri May 22 15:55:00 2020 fib ended at Fri May 22 15:57:19 2020 fac Starting at Fri May 22 15:57:19 2020 fac ended at Fri May 22 15:57:24 2020 sum Starting at Fri May 22 15:57:24 2020 sum ended at Fri May 22 15:57:30 2020 多线程(用时2分20秒): MultiThread executed func fib started at Fri May 22 15:57:30 2020 func fac started at Fri May 22 15:57:30 2020 func sum started at Fri May 22 15:57:30 2020 func fac ended at Fri May 22 15:57:35 2020 func sum ended at Fri May 22 15:57:36 2020 func fib ended at Fri May 22 15:59:50 2020 233 479001600 78 All done!!!'''
- 上述单线程就是顺序执行三个函数
- 多线程比单线程快了10秒
上述是基本的多线程基础编程,希望对各位有所帮助!