多线程

  1. 进程与线程的概念

    简单的说:进程就是运行着的程序,我们写的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执行多个线程对应的代码

  2. 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('主线程结束')

效果:

42342-p0e14ry6m6a.png

  1. 多线程共享数据

    做多线程开发,经常遇到这样的情况:多个线程里面的代码需要访问一个公共的数据对象。有时候,程序需要防止线程的代码同时操作公共的数据对象。否则就有可能导致数据的访问相互冲突影响

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)

结果:

52651-v37tjybj11c.png

很明显运行结果没有达到预期,这时,我们可以使用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)

结果(当执行到获取锁的位置,其他线程等待):

68228-kkjogxy65x.png

最后修改:2022 年 04 月 29 日
如果觉得我的文章对你有用,请随意赞赏