多线程
进程与线程的概念
简单的说:进程就是运行着的程序,我们写的python程序(或者其他应用程序),运行起来就称之为一个进程;在windows上,当程序还没有运行的时候,他们的程序代码文件存储在磁盘中,就是那些扩展名为.exe的文件
而系统中每个进程里面至少包含一个线程
线程是操作系统创建的,每个线程对应一个代码执行的数据结构,保存了代码执行过程中的重要的状态信息。没有线程,操作系统没法维护和管理代码运行的状态信息,所以没有创建进程之前,操作系统是不会执行我们的代码的
我们前面写的python程序,里面虽然没有创建线程的代码,但实际上,当python解释器程序运行起来(成为一个进程),OS就自动的创建一个线程,通常称为主线程,在这个主线程里面执行代码指令。当解释器执行我们的python程序代码的时候,我们的代码就在这个主线程中解释执行
现代计算机上面,CPU是多核的,每个核都可以执行代码,要运行程序里面的代码,操作系统会分配一个CPU核心去执行该代码。有时我们希望能够让更多的CPU核心同时执行我们程序里面的一些代码。比如程序里有一个compress函数执行压缩任务,现在有4个大文件需要压缩。如果是一个CPU执行这个函数,压缩一个文件要10秒的话,那么压缩4个文件就要40秒。而如果我们能够让4个CPU核心同事去执行压缩函数,理论上只要10秒
有时候,我们需要到前程无忧网站抓取python开发相关的职位信息,我们要抓取几百个网页的内容,执行这些抓取信息的任务的代码,时间主要耗费在等待网站返回信息上面。等待信息返回的时候CPU是空闲的。如果我们能用100个线程,同时运行获得网页的代码,理论上可以减少100倍的执行时间
要让多个CPU核心同时去执行任务,我们的程序必须创建多个线程,让CPU执行多个线程对应的代码
python中创建新线程
应用程序必须通过操作系统提供的系统调用,请求操作系统分配一个新的线程,python3将系统调用线程的功能封装在标准库threading中
from threading import Thread
from time import sleep
#定义一函数,作为新线程执行的入口函数
def threadFunc(arg1, arg2):
print('子进程开始')
print(f'线程函数参数是:{arg1},{arg2}')
sleep(5)
print('子进程结束')
#创建Thred类的实例对象,并且指定新线程的入口函数
thread = Thread(target=threadFunc, args=('参数1','参数2'))
#执行strat方法,就会创建新线程,新线程会执行入口函数的代码,就有了两个线程
thread.start()
#主线程的代码执行子线程对象的join方法,就会等待子线程结束才会继续执行下面的代码
thread.join()
print('主线程结束')
效果:
多线程共享数据
做多线程开发,经常遇到这样的情况:多个线程里面的代码需要访问一个公共的数据对象。有时候,程序需要防止线程的代码同时操作公共的数据对象。否则就有可能导致数据的访问相互冲突影响
from threading import Thread
from time import sleep
bank = {
'byhy': 0
}
#定义一个函数,作为新线程执行的入口函数
def deposit(theadidx, amount):
balance = bank['byhy']
#执行一些任务,耗费了0.1秒
sleep(0.1)
bank['byhy'] = balance + amount
print(f'子线程 {theadidx} 结束')
threalist = []
for idx in range(10):
thread = Thread(target=deposit, args=(idx, 1))
thread.start()
#把线程对象都存储到threadlist中
threalist.append(thread)
结果:
很明显运行结果没有达到预期,这时,我们可以使用threading库里面的锁对象Lock去保护,修改代码如下:
from threading import Thread, Lock
from time import sleep
bank = {
'byhy': 0
}
banklock = Lock()
#定义一个函数,作为新线程执行的入口函数
def deposit(theadidx, amount):
#操作共享数据时,申请获取锁
banklock.acquire()
balance = bank['byhy']
#执行一些任务,耗费了0.1秒
sleep(0.1)
bank['byhy'] = balance + amount
print(f'子线程 {theadidx} 结束')
#操作完共享数据后,申请释放锁
banklock.release()
threalist = []
for idx in range(10):
thread = Thread(target=deposit, args=(idx, 1))
thread.start()
#把线程对象都存储到threadlist中
threalist.append(thread)
结果(当执行到获取锁的位置,其他线程等待):