python中gil锁和线程锁,python gil锁对多进程有影响吗

  python中gil锁和线程锁,python gil锁对多进程有影响吗

  在本文中,我们将学习线程池和全局锁的创建。在进程池中创建线程池的原理是一样的;关于GIL全球锁,暂时没有代码练习,只是简单的启蒙一下它的概念。有兴趣的可以看看。

  00-1010线程池线程池的创建——并发线程池的常用方法线程池演示案例线程锁利用线程池实现抽奖小案例GIL全局锁GIL的作用

  

目录

  

线程池

  Concurrent是Python内置的一个包,可以帮助我们完成创建线程池的任务。

  方法介绍举例期货。threadpooleexecutor通过调用并发包的futures模块的threadpooleexecutor类来创建线程池tpool=threadpooleexecutor(max _ workers),创建线程池的对象是通过实例化threadpooleexecutor来实现的,threadpooleexecutor有一个参数设置线程池的数量。这与创建的进程池设置的数量完全相同。

  

线程池的创建 - concurrent

  让我们来看看线程池对象中的常用方法:

  函数名称用法介绍submit向线程池添加任务submit(target,args)done确认线程池中的某个线程是否完成了任务done()rsult获取当前线程执行任务的结果result()submit函数:通过submit函数传入参数;这个函数传入的参数也被传入要执行的函数和这个函数的参数。因为它的参数不需要以赋值语句的形式传入,所以只需要传入相应的值即可(后面会进行一个练习)。Done函数:判断当前线程是否结束;是bool类型的返回值。结果函数:返回当前线程池中线程任务的执行结果。这样就可以得到线程池的返回值。

  

线程池的常用方法

  1.定义一个函数实现循环的效果。

  2.定义一个线程池并设置线程数量。

  #编码:utf-8

  导入时间

  从concurrent.futures导入线程池执行器

  国防工程(一):

  打印(第{}个周期)。格式(一))

  Time.sleep(1) #之所以每次都要用睡眠功能,是因为功能执行速度太快;试着通过睡眠来长时间模拟一个任务的执行。

  if __name__==__main__:

  thread _ poor=threadpooleexecutor(4)#实例化一个线程池,将线程数设置为4。

  对于范围(20):内的I

  Thread_poor.submit(work,(I,))#使用submit函数向work函数添加任务。

  运行效果如下

  从运行结果来看,我们的线程任务一次执行四个任务,然后阻塞后面四个线程一秒钟是没有问题的。

  PS:需要注意的是,运行结果可能是两个或多个任务的结果打印在同一行。这是因为多线程的任务是同时处理的,也就是所谓的‘并发’。

  

线程池演示案例

  前一个进程池与进程锁匹配,同一个线程池也有对应的线程锁。线程锁的使用方式几乎和进程锁一样,只是线程锁对应于线程。

  1.实例化线程锁

  2.在工作函数中调用线程锁

  /p>

  3、并获取线程的返回值(线程池也是可以获取返回值的)

  代码示例如下:

  

 # coding:utf-8

  import os

  import time

  import threading

  from concurrent.futures import ThreadPoolExecutor

  lock = threading.Lock() # 全局定义一个 Lock() 实例

  def work(i):

   lock.acquire() # 区别于 进程锁 只需要在全局实例化一个即可,线程锁需要在线程任务的函数中调用 线程锁 才会生效

   print(当前是第 {} 次循环.format(i))

   time.sleep(1) # 之所以每次都要使用 sleep 函数,是因为函数执行太快;通过 sleep 尝试模拟一下长时间的执行一个任务

   lock.release()

   return 第 {} 次循环的进程id为:{}.format(i, os.getpid()) # 线程也是基于进程实现的

  if __name__ == __main__:

   thread_poor = ThreadPoolExecutor(4) # 实例化一个线程池,设置线程数量为4

   result = []

   for i in range(20):

   result_thread = thread_poor.submit(work, (i,)) # 利用 submit 函数将任务添加至 work 函数;

   # 需要注意的是这里不像进程池那样使用赋值的形式传入 work 函数

   result.append(result_thread)

   for res in result:

   print(res.result())

  

  运行结果如下:

  

  从运行结果可以看到,之前一同执行的4个任务现在变成了一次只执行一个任务;每一个个线程都是在主进程 93215下执行的,说明线程与进程还是有所区别的,虽然我们有多个线程任务在执行,但是依然是在主进程下去完成的;同时我们还获取到了 线程的返回值 第 {} 次循环的进程id为:{}'.format(i, os.getpid() 。

  以上就是线程池的使用和常用方法,我们会发现线程池的使用实际上要比进程池的使用要容易一些。进程池我们需要考虑 join 与 close 等一些问题,但是线程池则不需要那么的严格,并且线程相对于进程要更加的轻量,使用起来也更加的便捷。

  

  

利用线程池实现抽奖小案例

  案例代码如下:

  

# coding:utf-8

  import threading

  import random

  from concurrent.futures import ThreadPoolExecutor

  lock = threading.Lock()

  def luck_draw(arg):

   lock.acquire()

   # 从手机列表中随机选出一个中奖手机,其他手机均未中奖

   phone = random.choice(arg[0])

   # 在从奖池中随机选取一个奖品,视为该手机抽中的奖品

   price = random.choice(arg[1])

   prices.remove(price)

   phones.remove(phone)

   lock.release()

   return 恭喜手机尾号为{}的用户,抽到{}.format(str(phone)[-5:-1], price)

  if __name__ == __main__:

   t = ThreadPoolExecutor(3) # 通过创建三个线程从而实现每个线程完成一项抽奖任务

   # 确定抽奖人数

   phone_num = int(input(请输入抽奖的用户人数:))

   # 模拟产生出相应数量的手机号

   phones = random.sample(range(13300000000, 19999999999), phone_num) # 这里设置的随机号码仅做演示效果

   prices = [一等奖:iPhone12 ProMax, 二等奖:ipad2021pro, 三等奖:air wetter]

   result = []

   for i in range(3): # 三个任务,每个线程分配一个

   t_result = t.submit(luck_draw, (phones, prices))

   result.append(t_result)

   for res in result:

   print(res.result())

  

  运行效果如下:

  

  

  

GIL全局锁

  本章节的开头我们就说过,该部分没有代码的相关练习。仅仅是对 GIL全局锁 做一个概念上的简单启蒙。

  其他语言的线程与Python线程的区别

  多线程与多进程的使用其实是比较复杂的,目前作为初学者来说涉及的还比较浅。最近的几个章节介绍了 进程与线程在CPU的执行方式,这里再进行拓展一下。

  下面我们看一张图:

  

  依然是一个CPU 与4个核心(可以认为是4条跑道);

  先看左边的两条跑道,是进程1创建的3个线程。这三条线程有一个去了 1core 跑道,另外两条则去了 2core 跑道。线程之间有选择性的进入了不同的跑道,当然进程1的主进程或者说是主线程可能会在 1core 跑道、也可能会在 2core 跑道,这是其他语言进行多线程的样子。

  再来看看右边,Python 创建的进程2。当进程创建之后,包含主线程一共产生了3个线程,而这三个线程都跑到了 4core 跑道 上去。它不会像其他语言那样去寻找不同的有空闲资源的跑道去执行,而是仅仅在主进程所处的跑道去执行线程。造成 Python 中的多线程无法在多条跑道执行任务的主要原因就是因为 GIL全局锁 ,这个 GIL 并不是 Python语法中添加上去的,而是 python解释器 在执行的时候自动加了这把 "锁" 。

  

  

GIL 的作用

  因为 GIL 锁 的关系,使得 Python 的多线程无法在多个CPU跑道上去执行任务,它只能在单一CPU上进行工作。

  这也限制了多线程的性能,毕竟 Python 的多线程只能在一条跑道上运行。跑道满了,运行速度依然会慢。而在多个跑道上运行的任务必然是要比单一跑道效率会高很多。

  Python创始人 Guido 之所以保留 GIL 锁,其实也是为了线程之间的安全。虽然这个话题一直都在争论,不过我们也有办法去掉这个 GIL全局锁。

  默认的解释器是 Python 自带的解释器,这里我们可以选择一个叫做 pypy 的解释器。通过它来执行 Python 脚本是不含有 GIL全局锁 的,但并不太推荐这种做法。

  另外一种解决方法就是使用 多进程 + 多线程 的方式 来弥补这一短板上的问题,通过多进程在每个 CPU 跑道上执行任务,并且每个进程的跑道上再去执行多个线程。 ,让它们在各自的时间片上去运行。这些用法会在后续的章节会介绍到,在此只需要了解即可。

  以上就是Python学习之线程池与GIL全局锁详解的详细内容,更多关于Python线程池 GIL全局锁的资料请关注盛行IT软件开发工作室其它相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: