编程的乐趣在于让程序越来越快,这里将给大家介绍一个种加快程序运行的的编程方式——多线程

 

1 著名的全局解释锁(GIL)

说起python并发编程,就不得不说著名的全局解释锁(GIL)了。有兴趣的同学可以我查找一下相关的资料了解一下GIL,在这里大家只要知道一点,因为GIL的存在, 对于任何Python程序,不管有多少的处理器,任何时候都总是只有一个线程在执行。
下面先看一个例子:
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Mon Jun 5 16:12:14 2017
  5.  
  6. @author: 80002419
  7. """
  8. import threading
  9. import time
  10.  
  11. def cost(fun):##定义一个装饰器,用来计算函数运行的时间
  12. def wrapper(*args,**kargs):
  13. before_tm = time.time()
  14. fun(*args,**kargs);
  15. after_tm = time.time()
  16. fun.__doc__
  17. print("{0} cost:{1}".format(fun.__name__,after_tm-before_tm))
  18. return wrapper
  19.  
  20. def fibs1(n):
  21. if (n == 1):
  22. return 0
  23. elif(n == 2):
  24. return 1
  25. else:
  26. return fibs1(n-2)+fibs1(n-1)
  27.  
  28. def fibs2(n):
  29. list = []
  30. if (n == 1):
  31. list = [0]
  32. return list[-1]
  33. elif(n == 2):
  34. list = [0,1]
  35. return list[-1]
  36. else:
  37. list = [0,1]
  38. for i in range(2,n):
  39. list.append(list[i-1]+list[i-2])
  40. return list[-1]
  41.  
  42. @cost
  43. def nothread():
  44. fibs1(35)
  45. fibs1(35)
  46.  
  47.  
  48. #@cost
  49. #def nothread1():
  50. # print(fibs2(40))
  51. # print(fibs2(40))
  52.  
  53. @cost
  54. #使用多线程的程序
  55. def inthread():
  56. threads = []
  57. for i in range(2):
  58. t = threading.Thread(target = fibs1,args =(35,))
  59. t.start()
  60. threads.append(t)
  61. for t in threads:
  62. t.join()
  63.  
  64. @cost
  65. def inthread1():
  66. for i in range(2):
  67. t = threading.Thread(target=fibs1, args=(35,))
  68. t.start()
  69. main_thread = threading.currentThread()
  70. for t in threading.enumerate():
  71. if t is main_thread:
  72. continue
  73. t.join()
  74.  
  75. nothread()
  76. inthread()

打印结果:

nothread cost:5.41788887978
inthread cost:14.6728241444
 
上面的例子对比了一个cpu密集型程序分别使用多线程和不使用多线程的情况,我们可以得到结论:
对于cpu密集型任务,使用多线程反而会减慢程序的运行。
这是为什么呢?
 因为GIL在任何时候,仅仅允许一个单一的线程能够获取Python对象或者C API。每100个字节的Python指令解释器将重新获取锁,这(潜在的)阻塞了I/O操作
同时 
GIL是必须的,这是Python设计的问题:Python解释器是非线程安全的。这意味着当从线程内尝试安全的访问Python对象的时候将有一个全局的强制锁。当然python

多线程也有可以加快程序运行的时候:当我们开发和网络通信或者数据输入/输出相关的程序,比如网络爬虫、文本处理等等。这时候由于网络情况和I/O的性能的限制,Python解释器会等待读写数据的函数调用返回,这个时候就可以利用多线程库提高并发效率了。这类任务我们称为IO(密集型任务)

 

2  Semaphore类 ——python对象访问量的控制

在多线程编程中,为了防止不同的线程同时对一个公用的资源(比如全部变量)进行修改,需要进行同时访问的数量(通常是1)。信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。
下面来看一个例子:
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Mon Jun 5 16:12:14 2017
  5.  
  6. @author: 80002419
  7. """
  8. from threading import Thread, Semaphore
  9. import time,threading
  10.  
  11. sema = Semaphore(3) # 定义一个信号量为3的Semaphone对象,
  12.  
  13. #定义一个测试函数作为,被访问的对象
  14. def test(tid):
  15. with sema:
  16. print("信号量:{0}".format(tid))
  17. time.sleep(0.5)
  18. print('release!')
  19.  
  20. thds = []
  21. for i in range(6):
  22. t = Thread(target = test,args=(i,))
  23. t.start()
  24. thds.append(t)
  25.  
  26. for t in thds:
  27. t.join()

打印结果:

信号量:2
信号量:1

信号量:0

release!

信号量:0

release!

信号量:0

release!

信号量:0

release!

release!

release!
 
结果分析 :
当信号量sema信号为0时,此时不再不允许进程对test 再进行访问了,release 之后才能再继续生成亲的进程对其访问,
Semaphore类的作用就是限制公共资源的访问量
 
3 锁——lock 与 Rlock
先看一段代码:
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Mon Jun 5 16:12:14 2017
  5.  
  6. @author: 80002419
  7. """
  8. from threading import Thread, Lock
  9. import time,threading
  10.  
  11. value = 0;
  12.  
  13. def changeValue():
  14. global value
  15. new = value + 1
  16. time.sleep(0.001)#让其它进程可以切换进来
  17. value = new
  18.  
  19. thds = []
  20. for _ in range(100):
  21. t = Thread(target = changeValue)
  22. t.start()
  23. thds.append(t)
  24.  
  25. for t in thds:
  26. t.join()
  27.  
  28. print(value)

打印结果:

18
结果分析: 多个线程同时访问 value 变量,经过cpu存储后,再写回内存,如果进程A,进程B,在访问value时 值为0 ,进程A经过函数处理后,value = 1 ,再写回内存,value =1 ,进程B 执行完成后,会刷新value 但是写回的value 还是1, 所以并不能保护执行了100次changeValue后,结果为100
 
那么我们怎么能保证执行的结果就是100呢:再看一段代码:
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Mon Jun 5 16:12:14 2017
  5.  
  6. @author: 80002419
  7. """
  8. from threading import Thread, Lock
  9. import time,threading
  10.  
  11. value = 0
  12.  
  13. lock = Lock()
  14.  
  15. def changeValue():
  16. global value
  17. with lock:
  18. new = value + 1
  19. time.sleep(0.001)#让其它进程可以切换进来
  20. value = new
  21.  
  22. thds = []
  23.  
  24. for _ in range(100):
  25. t = Thread(target = changeValue)
  26. t.start()
  27. thds.append(t)
  28. for t in thds:
  29. t.join()
  30. print(value)

打印结果:

100
Lock也可以叫做互斥锁,其实相当于信号量为1,上面例子lock 也就可以用 sema = Semaphore()代替
 
本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理

想要获取更多Python学习资料可以加
QQ:2955637827私聊
或加Q群630390733
大家一起来学习讨论吧!
 

python并发编程——多线程的更多相关文章

  1. python并发编程&多线程(二)

    前导理论知识见:python并发编程&多线程(一) 一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性 官网链 ...

  2. python并发编程&多线程(一)

    本篇理论居多,实际操作见:  python并发编程&多线程(二) 一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一 ...

  3. python 并发编程 多线程 目录

    线程理论 python 并发编程 多线程 开启线程的两种方式 python 并发编程 多线程与多进程的区别 python 并发编程 多线程 Thread对象的其他属性或方法 python 并发编程 多 ...

  4. Python并发编程——多线程与协程

    Pythpn并发编程--多线程与协程 目录 Pythpn并发编程--多线程与协程 1. 进程与线程 1.1 概念上 1.2 多进程与多线程--同时执行多个任务 2. 并发和并行 3. Python多线 ...

  5. python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03

    目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...

  6. python 并发编程 多线程 GIL与多线程

    GIL与多线程 有了GIL的存在,同一时刻同一进程中只有一个线程被执行 多进程可以利用多核,但是开销大,而python的多线程开销小,但却无法利用多核优势 1.cpu到底是用来做计算的,还是用来做I/ ...

  7. python 并发编程 多线程 GIL全局解释器锁基本概念

    首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念. 就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码. ...

  8. python 并发编程 多线程 GIL与Lock

    GIL与Lock Python已经有一个GIL来保证同一时间只能有一个线程来执行了,为什么这里还需要互斥锁lock? 锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据 GIT保证了 ...

  9. python 并发编程 多线程 线程queue

    线程queue 线程之间已经是共享数据的,为什么还使用线程queue? 线程需要自己加锁,线程queue帮我们处理好加锁的问题 有三种不同的用法 第一种方法: class queue.Queue(ma ...

随机推荐

  1. Elasticsearch 理解mapping中的store属性

    默认情况下,对字段值进行索引以使其可搜索,但不存储它们 (store). 这意味着可以查询该字段,但是无法检索原始字段值.在这里我们必须理解的一点是: 如果一个字段的mapping中含有store属性 ...

  2. 安装git和lsof

    yum install git yum install lsof 查看80端口 lsof -i:80

  3. 2016年第七届蓝桥杯【C++省赛B组】F、G、H、J 题解

    F. 方格填数 #深搜 题意 有\(10\)个格子,填入0~9的数字.要求:连续的两个数字不能相邻.(左右.上下.对角都算相邻),求可能的填数方案数. +--+--+--+ | | | | +--+- ...

  4. Java基础教程——模拟B/S结构的服务器

    浏览器发送请求,用Java模拟写个简单的服务器,浏览器可以收到响应. 执行下列代码(设置static final int TEST = 1), 使用浏览器访问:http://127.0.0.1:888 ...

  5. 那么多人学习C++,学习它有什么好处?学完以后能从事哪些岗位?

    相信很多人接触编程都是源于大学期间的那堂C++语言程序编程,但是这门课却只告诉了你编程语言是什么,却没告诉你要怎么去熟练掌握编程.所以,不可避免的是许多人在毕业前夕才发现虽然学会了C++,但是好像却不 ...

  6. 团队作业第三次 —— UML设计

    这个作业要求在哪里 https://edu.cnblogs.com/campus/fzzcxy/2018SE2/homework/11366 这个作业的目标 <团队一起设计UML图> 团队 ...

  7. 03Python网络编程之单线程服务端

    # 对于单线程的服务端,我们借助于zen_utils(我们自己编写好的一些函数)是很容易就实现的.# 导入这个模块import zen_utilsif __name__ == '__main__': ...

  8. 【五校联考1day2】JZOJ2020年8月12日提高组T1 对你的爱深不见底

    [五校联考1day2]JZOJ2020年8月12日提高组T1 对你的爱深不见底 题目 Description 出乎意料的是,幸运E 的小R 居然赢了那个游戏.现在欣喜万分的小R 想要写一张明信片给小Y ...

  9. Django搭建示例项目实战与避坑细节

    Django 开发项目是很快的,有多快?看完本篇文章,你就知道了. 安装 Django 前提条件:已安装 Python. Django 使用 pip 命令直接就可以安装: pip install dj ...

  10. 喜欢 Dapper 的朋友看过来,送一份厚礼

    写在开头 众所周知 Dapper 是 .NET 下最轻最快的 ORM,它是喜欢写 SQL 码农的福音,相对于 SqlHelper 它更加方便,据统计 10个 .NETer 有 9个 用过 Dapper ...