注册 登录

清河洛

python中多线程的实现

qingheluo2019-04-11清河洛234
进程:进程简单来说就是计算机中正在执行的程序,每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。进程由计算机上的操作系统来管理执行,分配时间。进程之间共享信息并不是十分轻松,因为每个进程之间都是相互独立的,它们使用IPC这种方式共享信息。线程:线程是在同一个进程下执行的,信息可以很方便的在此进程和其下的线程中共享。因为线程一般并发执行,所以可以多个任务共同执行,减少程序的运行速度。这种并发其实是一种伪并发,计算机中并不能实现真正的同时执行,这种并发的实现机制其实是当一个线程运行短暂的时间后,让步下一个待运行的线程,自身重新去排队等待。这种机制下,由于数据是共享的,...

进程:进程简单来说就是计算机中正在执行的程序,每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。进程由计算机上的操作系统来管理执行,分配时间。进程之间共享信息并不是十分轻松,因为每个进程之间都是相互独立的,它们使用IPC这种方式共享信息。

线程:线程是在同一个进程下执行的,信息可以很方便的在此进程和其下的线程中共享。因为线程一般并发执行,所以可以多个任务共同执行,减少程序的运行速度。这种并发其实是一种伪并发,计算机中并不能实现真正的同时执行,这种并发的实现机制其实是当一个线程运行短暂的时间后,让步下一个待运行的线程,自身重新去排队等待。这种机制下,由于数据是共享的,多个线程并发的共同操作同一个对象时在没有约束的情况下是有风险的,必须对线程的并发进行一些约束(这就要引入锁的概念了)。

进程是由若干线程组成的,一个进程至少有一个线程。

线程是操作系统直接支持的执行单元,一般高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程。

线程不安全:多线程中如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期。这种现象叫做“线程不安全”

给一个列表a的第4个元素赋值,这个操作的步骤是
    1、在内存中创建要赋予的值
    2、定位到a列表的第4个元素
    3、把a[3]指向内存中的值
现在有2个线程同时对a变量第4个元素赋值,线程1赋值为1,线程2赋值为2
我们首先start了线程1,因为线程2是后运行的,所以我们预期a[3]最后为2
但是由于python的多线程实际上就是在多个线程之间快速切换,如果线程1运行赋值操作的第2步线程切换到了线程2,那么最后的结果就不可预料了,这个就是线程不安全

给资源加锁其实并不是给资源加锁,而是用锁去锁定资源,你可以定义多个锁,当需要独占某一资源时,任何一个锁都可以锁这个资源就好比你用不同的锁都可以把相同的一个门锁住是一个道理。

要支持多线程的话,一个基本的要求就是不同线程对共享资源访问的互斥以避免“线程不安全”,所以Python中引入了GIL(Global Interpreter Lock),Python中的GIL是一个非常霸道的互斥实现,在一个线程拥有了解释器的访问权之后,其它的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响。这也就意味着,无论如何,在同一时间,只能有一个线程能访问Python提供的API。因为单处理器的本质是不可能并行的,这里的同一时间确实对于单处理器是毫无意义的,但是对于多处理器,同一时间,确实可以有多个时间独立运行。然而正是由于GIL限制了这样的情形,使得多处理器最终退化为单处理器,性能大打折扣。

目前为止,GIL仍然是Python多线程机制的基石。对于Python而言,字节码解释器是Python的核心所在,所以Python通过GIL来互斥不同线程对解释器的使用:

假设,现在有三个线程A、B和C,它们都需要解释器来执行字节码进行对应的计算,那么在这之前,它们必须获得GIL。那么现在假设线程A获得了GIL,其它线程只能等A释放GIL之后,才能获得。于是,有两个问题:

  1. 线程A何时释放GIL(如果A使用完解释器之后才释放GIL,那么并行的计算退化为串行,多线程没有了意义)
  2. 线程B和C谁将在A释放GIL之后获得GIL呢?

第一个问题:

操作系统进行进程切换是当一个进程执行了一段时间之后,发生了时钟中断,于是操作系统响应时钟中断,并在这时开始进程的调度。与此类似,Python中通过软件模拟了这样的中断,来激活线程的调度。

Python内部维护着这样一个数值,作为Python内部的时钟,假设这个值为N,那么Python将在执行了N秒之后立刻启动线程调度机制。也就是说,当一个线程获得GIL后,Python内部的监测机制就开始启动,当这个线程执行了N秒后,Python解释器将强制挂起当前线程,开始切换到下一个处于等待状态的线程。Python在多线程其实就是线程之间的快速切换。

在Python中,sys模块的getswitchinterval()方法可以获得这个间隔数值N(默认为0.005秒)

下一个问题,Python会在众多等待的线程中选择哪一个呢?

答案是不确定,被堵塞的线程是没有顺序的,究竟哪个线程会被选中这个问题是交给了底层的操作系统来解决的,Python借用了底层操作系统所提供的线程调度机制来决定下一个获得GIL进入解释器的线程是谁。所以说,Python中的线程实际上就是操作系统所支持的原生线程。



网址导航