今天花了近乎一天的时间研究python关于多线程的问题,查看了大量源码 自己也实践了一个生产消费者模型,所以把一天的收获总结一下。

由于GIL(Global Interpreter Lock)锁的关系,纯的python代码处理一般逻辑的确无法活动性能上的极大提升,但是在处理需要等待外部资源返回或多用户的应用程序中,多线程仍然可以作为一个比较好的工具来进行使用。

python提供了两个模块thread和threading 来支持python的多线程操作。通俗的讲一般现在我们只使用threading模块来编程了,thread模块定义了很多原始行为,更接近底层,而threading模块抽象了thread模块可用性更好,同时提供更多特性。

现在创建线程的通用方法一般是创建一个类并且继承threading.Thread,然后重写其中的__init__和run()方法。 更多详情可以参考threading模块代码内注释以及代码。下面直接看个例子。

  1. import time
  2. import threading
  3.  
  4. class Test(threading.Thread):
  5. def __init__(self, name, delay):
  6. super(Test, self).__init__()
  7. self.name = name
  8. self.delay = delay
  9.  
  10. def run(self):
  11. print "%s delay for %s seconds" % (self.name, self.delay)
  12. time.sleep(self.delay)
  13. c = 0
  14. while True:
  15. print "This is thread %s on line %s" % (self.name, c)
  16. c += 1
  17. if c == 3:
  18. print "End of thread %s" % self.name
  19. break
  20.  
  21. t1 = Test('Thread1', 5)
  22. t2 = Test('Thread2', 5)
  23.  
  24. t1.start()
  25. print 'Wait t1 to end'
  26. t1.join()
  27. t2.start()
  28. t2.join()
  29. print 'End of main'

注意一下这一句 :

  1. super(Test, self).__init__()

这是按照模块要求,必须初始化父类的__init__函数 所以使用了super()

其他并没有值得多少的地方,

创建线程方便的实例化自己写的继承threading.Thread的类 然后传入对应的参数。

最后使用xxx.start()来运行线程。 使用xxx.join()来阻塞线程。

特别注意的是。继承自Threading类的子类还有一个daemon参数,如果这个参数适用setDaemon()方法置为True之后,主线程将不会等待子线程都结束之后才结束,而是自己运行完之后就结束,这种方式相当粗暴。 如果将daemon参数设置为False的话,主线成将会等待所有子线程结束之后再结束。daemon属性可以通过使用isDaemon()方法获取一个boolean值。

更进一步的,我必须介绍一下线程之间的同步和互斥问题。下面引用《计算机操作系统》中的介绍。

当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。

(1)间接相互制约。一个系统中的多个线程必然要共享某种系统资源,如共享CPU,共享I/O设备,所谓间接相互制约即源于这种资源共享,打印机就是最好的例子,线程A在使用打印机时,其它线程都要等待。

(2)直接相互制约。这种制约主要是因为线程之间的合作,如有线程A将计算结果提供给线程B作进一步处理,那么线程B在线程A将数据送达之前都将处于阻塞状态。

间接相互制约可以称为互斥,直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步

在一段时间内只允许一个线程访问的资源就称为临界资源或独占资源,计算机中大多数物理设备,进程中的共享变量等待都是临界资源,它们要求被互斥的访问。每个进程中访问临界资源的代码称为临界区。

这里为了介绍这种稍微复杂的概念。 再列出一个生产消费者的例子 使用到了Queue队列。

  1. # coding:utf-8
  2. import Queue
  3. import time
  4. import random
  5. import threading
  6.  
  7. # write_lock = threading.Lock() # 创建primitive锁对象用于控制输出
  8.  
  9. class Producer(threading.Thread):
  10. # q传递一个队列参数, con传递了一个链接, name传递了一个名字
  11. def __init__(self, q, con, name):
  12. super(Producer, self).__init__()
  13. self.q = q
  14. self.con = con
  15. self.name = name
  16. print "Producer " + self.name + "Started"
  17.  
  18. def run(self):
  19. while True:
  20. # 锁对象常用的acquire获得锁方法和release释放锁方法
  21. # 这里使用的是Thread的Condition对象
  22. self.con.acquire()
  23. if self.q.full():
  24. print 'Queue is full, producer wait!'
  25.  
  26. # 手动挂起,并且只能在获得Lock的情况下才可以使用 否则会触发RuntimeError
  27. # 调用wait()会释放Lock 直到该线程被notify(),notifyall()或超时该线程又重新获得Lock
  28. self.con.wait()
  29. else:
  30. value = random.randint(0, 10)
  31. print self.name + " put " + str(value) + "into queue"
  32. self.q.put((self.name+":"+str(value))) # 放置到队列中
  33.  
  34. # 通知消费者,notify通知其他线程,被挂起的线程接到通知后会开始运行
  35. # 默认通知一个正在等待该condition的线程,最多唤醒n个线程 必须在获得Lock的情况下使用否则会报错.
  36. self.con.notify()
  37. self.con.release() # 释放锁对象
  38.  
  39. class Consumer(threading.Thread):
  40. def __init__(self, q, con, name):
  41. super(Consumer, self).__init__()
  42. self.q = q
  43. self.con = con
  44. self.name = name
  45. print "Consumer " + self.name + "started\n"
  46.  
  47. def run(self):
  48. while True:
  49. # Condition常用的acquire获得条件和release释放锁方法
  50. self.con.acquire()
  51. if self.q.empty():
  52. print 'queue is empty, consumer wait!'
  53. self.con.wait()
  54. else:
  55. value = self.q.get() # 从队列中取消息
  56. print self.name + " get " + value + "from queue"
  57.  
  58. # 发送消息通知生产者
  59. self.con.notify()
  60. self.con.release() # 释放锁对象
  61. print 'queue still have ' + str(q.qsize()) + 'task\n'
  62.  
  63. if __name__ == "__main__":
  64. q = Queue.Queue(10)
  65.  
  66. # 使用Condition对象可以在某些事件触发或达到特定的条件后才处理数据.
  67. con = threading.Condition()
  68.  
  69. # 两个生产者
  70. p1 = Producer(q, con, "P1")
  71. p2 = Producer(q, con, "P2")
  72. c1 = Consumer(q, con, "C1")
  73. p2.start()
  74. p1.start()
  75. c1.start()

可以看到这是一个典型的生产消费者模型。 两个生产者负责往队列中添加数据,一个消费者复杂从队列里面拿出数据处理掉然后继续拿出继续处理,直到队列为空然后挂起等待生产者生产。

要解决同步和互斥其实最简单的办法就是引入一个锁的机制。在一个线程访问一个共享资源的时候,就将这个资源锁住,直到该线程访问完毕之后再释放给其他线程进行访问。这样就保证了,在一个线程处理一个共享资源的情况下,不会因为其他线程也在处理同一个资源而造成混乱。

可以看到这里我申明了con = threading.Condition() 申明了一个条件对象。而它拥有acquire()/release()/notify()/wait() 等方法。 分别是获取锁,释放锁,唤醒操作和等待操作。上面用到了这四种方法。可以看到我们在生产者Producer中,先获取了一个锁对象,然后来判断队列是否已满,如果满了则不再生产,并且挂起当前线程并且暂时释放掉获取的锁。 直到收到消费者的notify(),并且获得消费者释放的锁。

这样就很清楚了,通过锁的机制以及挂起的机制。我们可以更安全的进行多线程操作。 其实在这个例子中,queue本身是线程安全的。不需要额外的同步机制。所以我们可以完全不需要条件锁。也就是说我们不需要制造一个Condition对象,直接使用队列就可以轻松实现多线程的消费者和生产者模型了。

  1. # coding:utf-8
  2. import Queue
  3. import time
  4. import random
  5. import threading
  6.  
  7. # write_lock = threading.Lock() # 创建primitive锁对象用于控制输出
  8.  
  9. class Producer(threading.Thread):
  10. # q传递一个队列参数, con传递了一个链接, name传递了一个名字
  11. def __init__(self, q, name):
  12. super(Producer, self).__init__()
  13. self.q = q
  14. # self.con = con
  15. self.name = name
  16. print "Producer " + self.name + "Started"
  17.  
  18. def run(self):
  19. while True:
  20. # 锁对象常用的acquire获得锁方法和release释放锁方法
  21. # 这里使用的是Thread的Condition对象
  22. # self.con.acquire()
  23. if self.q.full():
  24. print 'Queue is full, producer wait!'
  25.  
  26. # 手动挂起,并且只能在获得Lock的情况下才可以使用 否则会触发RuntimeError
  27. # 调用wait()会释放Lock 直到该线程被notify(),notifyall()或超时该线程又重新获得Lock
  28. # self.con.wait()
  29. else:
  30. value = random.randint(0, 10)
  31. print self.name + " put " + str(value) + "into queue"
  32. self.q.put((self.name+":"+str(value))) # 放置到队列中
  33.  
  34. # 通知消费者,notify通知其他线程,被挂起的线程接到通知后会开始运行
  35. # 默认通知一个正在等待该condition的线程,最多唤醒n个线程 必须在获得Lock的情况下使用否则会报错.
  36. # self.con.notify()
  37. # self.con.release() # 释放锁对象
  38.  
  39. class Consumer(threading.Thread):
  40. def __init__(self, q, name):
  41. super(Consumer, self).__init__()
  42. self.q = q
  43. # self.con = con
  44. self.name = name
  45. print "Consumer " + self.name + "started\n"
  46.  
  47. def run(self):
  48. while True:
  49. # Condition常用的acquire获得锁方法和release释放锁方法
  50. # self.con.acquire()
  51. if self.q.empty():
  52. print 'queue is empty, consumer wait!'
  53. # self.con.wait()
  54. else:
  55. value = self.q.get() # 从队列中取消息
  56. print self.name + " get " + value + "from queue"
  57.  
  58. # 发送消息通知生产者
  59. # self.con.notify()
  60. # self.con.release() # 释放锁对象
  61. print 'queue still have ' + str(q.qsize()) + 'task\n'
  62.  
  63. if __name__ == "__main__":
  64. q = Queue.Queue(10)
  65.  
  66. # 使用Condition对象可以在某些事件触发或达到特定的条件后才处理数据.
  67. # con = threading.Condition()
  68.  
  69. # 两个生产者
  70. p1 = Producer(q, "P1")
  71. p2 = Producer(q, "P2")
  72. c1 = Consumer(q, "C1")
  73. p2.start()
  74. p1.start()
  75. c1.start()

以上内容参考链接:

1. python threading模块文档翻译: http://my.oschina.net/lionets/blog/194577?fromerr=pbWOeveo

2. 多线程7经典线程与互斥总结:http://blog.csdn.net/dazhong159/article/details/7927034

3. 《编写高质量代码改善python程序的91个建议》第48和49建议。

python threading模块使用 以及python多线程操作的实践(使用Queue队列模块)的更多相关文章

  1. Python第十五天 datetime模块 time模块 thread模块 threading模块 Queue队列模块 multiprocessing模块 paramiko模块 fabric模块

    Python第十五天  datetime模块 time模块   thread模块  threading模块  Queue队列模块  multiprocessing模块  paramiko模块  fab ...

  2. Python -- queue队列模块

    一 简单使用 --内置模块哦 import Queuemyqueue = Queue.Queue(maxsize = 10) Queue.Queue类即是一个队列的同步实现.队列长度可为无限或者有限. ...

  3. Python 源码分析:queue 队列模块

    起步 queue 模块提供适用于多线程编程的先进先出(FIFO)数据结构.因为它是线程安全的,所以多个线程很轻松地使用同一个实例. 源码分析 先从初始化的函数来看: 从这初始化函数能得到哪些信息呢?首 ...

  4. Python守护进程、进程互斥锁、进程间通信ICP(Queue队列)、生产者消费者模型

    知识点一:守护进程 守护进程:p1.daemon=True 守护进程其实就是一个“子进程“,守护=>伴随 守护进程会伴随主进程的代码运行完毕后而死掉 进程:当父进程需要将一个任务并发出去执行,需 ...

  5. queue队列模块

    import Queue myqueue = Queue.Queue(maxsize = 10) Queue.Queue类即是一个队列的同步实现.队列长度可为无限或者有限.可通过Queue的构造函数的 ...

  6. Python笔记:threading(多线程操作)

    Python的线程操作在旧版本中使用的是thread模块,在Python27和Python3中引入了threading模块,同时thread模块在Python3中改名为_thread模块,thread ...

  7. 一行 Python 实现并行化 -- 日常多线程操作的新思路

    春节坐在回家的火车上百无聊赖,偶然看到 Parallelism in one line 这篇在 Hacker News 和 reddit 上都评论过百的文章,顺手译出,enjoy:-) http:// ...

  8. python用parammiko模块实现linux的远程操作

    parammiko  可以实现远程的带密码登录,解决ssh远程登陆需要交互的问题 (当然很多其他的,如tcl也可以).但这个用python做比较简单 1.parammiko 的安装 1.1.依赖模块 ...

  9. python操作MySQL数据库的三个模块

    python使用MySQL主要有两个模块,pymysql(MySQLdb)和SQLAchemy. pymysql(MySQLdb)为原生模块,直接执行sql语句,其中pymysql模块支持python ...

随机推荐

  1. 【Codeforces Round 1132】Educational Round 61

    Codeforces Round 1132 这场比赛做了\(A\).\(B\).\(C\).\(F\)四题,排名\(89\). \(A\)题\(wa\)了一次,少考虑了一种情况 \(D\)题最后做出来 ...

  2. android.view.WindowManager$BadTokenException: Unable to add window

    这是在加载dialog时出现的一个异常.转载地址:http://hi.baidu.com/fbdfp/item/7dea2d0ade9121813d42e23d 扔了好久的android又开始断断续续 ...

  3. TCP/IP协议--TCP的超时和重传

    TCP是可靠传输.可靠之一体现在收到数据后,返回去一个确认.但是不能完全避免的是,数据和确认都可能丢失.解决这个办法就是,提供一个发送的重传定时器:如果定时器溢出时还没收到确认,它就重传这个报文段. ...

  4. Ionic下的JPush缺少统计代码问题解决方法

    用Ionic打包apk后安装到手机,收到缺少统计代码的提示,解决方法如下: 1. 找到了 platforms/android/src/com/ionichina/ioniclub/MainActiov ...

  5. EZ 2018 07 06 NOIP模拟赛

    又是慈溪那边给的题目,这次终于没有像上次那样尴尬了, T1拿到了较高的暴力分,T2没写炸,然后T3写了一个优雅的暴力就203pts,Rank3了. 听说其它学校的分数普遍100+,那我们学校还不是强到 ...

  6. 计算机网络什么是OSI7层模型、TCP/IP4层模型理解

    模型图解 应用层 就是最顶层的.通常指的应用程序初始走的协议比如有 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示层 主要对数据应用层的数据包进行加密 会话层 建立.管理. ...

  7. phpstorm 报错及解决

    1. 当项目里有大量 js 文件时,一旦编辑包含 js 的文件,phpstorm 会卡顿,甚至未响应 问题原因: 内存限制较小 解决方法一: 直接将弹出框中的红色部分修改为需要的内存限制,并选择 Sh ...

  8. 由一个“两次请求”引出的Web服务器跨域请求访问问题的解决方案

    http://blog.csdn.net/cnhnnyzhy/article/details/53128179 (4)Access-Control-Max-Age 该字段可选,用来指定本次预检请求的有 ...

  9. kvm虚拟化管理平台WebVirtMgr部署-完整记录(0)

    打算部署kvm虚拟机环境,下面是虚拟化部署前的一些准备工作: 操作系统环境安装1)修改内核模式为兼容内核启动[root@ops ~]# uname -aLinux openstack 2.6.32-4 ...

  10. zabbix问题记录

    zabbix部署好,在使用一段时间后,出现了不少报错,在此简单做一记录.1)Zabbix监控界面报错Lack of free swap space on Zabbix server”解决公司线上部署的 ...