转自:http://www.cnblogs.com/fnng/p/3489321.html

在使用多线程之前,我们首页要理解什么是进程和线程。

什么是进程?

  计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间。

什么是线程?

  线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中,共享相同的运行环境。我们可以想像成是在主进程或“主线程”中并行运行的“迷你进程”。

7.2.1、单线程

  在单线程中顺序执行两个循环。一定要一个循环结束后,另一个才能开始。总时间是各个循环运行时间之和。

onetherad.py

  1. from time import sleep, ctime
  2.  
  3. def loop0():
  4. print 'start loop 0 at:', ctime()
  5. sleep(4)
  6. print 'loop 0 done at:', ctime()
  7.  
  8. def loop1():
  9. print 'start loop 1 at:', ctime()
  10. sleep(2)
  11. print 'loop 1 done at:', ctime()
  12.  
  13. def main():
  14. print 'start:', ctime()
  15. loop0()
  16. loop1()
  17. print 'all end:', ctime()
  18.  
  19. if __name__ == '__main__':
  20. main()

运行结果:

  1. start loop 0 at: Mon Dec 23 09:59:44 2013
  2. loop 0 done at: Mon Dec 23 09:59:48 2013
  3. start loop 1 at: Mon Dec 23 09:59:48 2013
  4. loop 1 done at: Mon Dec 23 09:59:50 2013
  5. all end: Mon Dec 23 09:59:50 2013

  Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。threading基于Java的线程模型设计。锁(Lock)和条件变量(Condition)在Java中是对象的基本行为(每一个对象都自带了锁和条件变量),而在Python中则是独立的对象。

7.2.1、thread模块

mtsleep1.py

  1. import thread
  2. from time import sleep, ctime
  3. loops = [4,2]
  4. def loop0():
  5. print 'start loop 0 at:', ctime()
  6. sleep(4)
  7. print 'loop 0 done at:', ctime()
  8.  
  9. def loop1():
  10. print 'start loop 1 at:', ctime()
  11. sleep(2)
  12. print 'loop 1 done at:', ctime()
  13.  
  14. def main():
  15. print 'start:', ctime()
  16. thread.start_new_thread(loop0, ())
  17. thread.start_new_thread(loop1, ())
  18. sleep(6)
  19. print 'all end:', ctime()
  20.  
  21. if __name__ == '__main__':
  22. main()

  start_new_thread()要求一定要有前两个参数。所以,就算我们想要运行的函数不要参数,我们也要传一个空的元组。

  这个程序的输出与之前的输出大不相同,之前是运行了 6,7 秒,而现在则是 4 秒,是最长的循环的运行时间与其它的代码的时间总和。

运行结果:

  1. start: Mon Dec 23 10:05:09 2013
  2. start loop 0 at: Mon Dec 23 10:05:09 2013
  3. start loop 1 at: Mon Dec 23 10:05:09 2013
  4. loop 1 done at: Mon Dec 23 10:05:11 2013
  5. loop 0 done at: Mon Dec 23 10:05:13 2013
  6. all end: Mon Dec 23 10:05:15 2013

  睡眠 4 秒和 2 秒的代码现在是并发执行的。这样,就使得总的运行时间被缩短了。你可以看到,loop1 甚至在 loop0 前面就结束了。

  程序的一大不同之处就是多了一个“sleep(6)”的函数调用。如果我们没有让主线程停下来,那主线程就会运行下一条语句,显示“all end”,然后就关闭运行着 loop0()和 loop1()的两个线程并退出了。我们使用 6 秒是因为我们已经知道,两个线程(你知道,一个要 4 秒,一个要 2 秒)在主线程等待 6 秒后应该已经结束了。

  你也许在想,应该有什么好的管理线程的方法,而不是在主线程里做一个额外的延时 6 秒的操作。因为这样一来,我们的总的运行时间并不比单线程的版本来得少。而且,像这样使用 sleep()函数做线程的同步操作是不可靠的。如果我们的循环的执行时间不能事先确定的话,那怎么办呢?这可能造成主线程过早或过晚退出。这就是锁的用武之地了。

mtsleep2.py

  1. #coding=utf-8
  2. import thread
  3. from time import sleep, ctime
  4.  
  5. loops = [4,2]
  6.  
  7. def loop(nloop, nsec, lock):
  8. print 'start loop', nloop, 'at:', ctime()
  9. sleep(nsec)
  10. print 'loop', nloop, 'done at:', ctime()
  11. #解锁
  12. lock.release()
  13.  
  14. def main():
  15. print 'starting at:', ctime()
  16. locks =[]
  17. #以loops数组创建列表,并赋值给nloops
  18. nloops = range(len(loops))
  19.  
  20. for i in nloops:
  21. lock = thread.allocate_lock()
  22. #锁定
  23. lock.acquire()
  24. #追加到locks[]数组中
  25. locks.append(lock)
  26.  
  27. #执行多线程
  28. for i in nloops:
  29. thread.start_new_thread(loop,(i,loops[i],locks[i]))
  30.  
  31. for i in nloops:
  32. while locks[i].locked():
  33. pass
  34.  
  35. print 'all end:', ctime()
  36.  
  37. if __name__ == '__main__':
  38. main()

thread.allocate_lock()

  返回一个新的锁定对象。

acquire() /release()

  一个原始的锁有两种状态,锁定与解锁,分别对应acquire()和release() 方法。

range()

  range()函数来创建列表包含算术级数。

range(len(loops))理解:

  1. >>> aa= "hello"
  2.  
  3. #长度计算
  4. >>> len(aa)
  5. 5
  6.  
  7. #创建列表
  8. >>> range(len(aa))
  9. [0, 1, 2, 3, 4]
  10.  
  11. #循环输出列表元素
  12. >>> for a in range(len(aa)):
  13. print a
  14.  
  15. 0
  16. 1
  17. 2
  18. 3
  19. 4

  我们先调用 thread.allocate_lock()函数创建一个锁的列表,并分别调用各个锁的 acquire()函数获得锁。获得锁表示“把锁锁上”。锁上后,我们就把锁放到锁列表 locks 中。

  下一个循环创建线程,每个线程都用各自的循环号,睡眠时间和锁为参数去调用 loop()函数。为什么我们不在创建锁的循环里创建线程呢?有以下几个原因:(1) 我们想到实现线程的同步,所以要让“所有的马同时冲出栅栏”。(2) 获取锁要花一些时间,如果你的线程退出得“太快”,可能会导致还没有获得锁,线程就已经结束了的情况。

  在线程结束的时候,线程要自己去做解锁操作。最后一个循环只是坐在那一直等(达到暂停主线程的目的),直到两个锁都被解锁为止才继续运行。

mtsleep2.py运行结果:

  1. starting at: Mon Dec 23 20:57:26 2013
  2. start loop start loop0 1at: at:Mon Dec 23 20:57:26 2013
  3. Mon Dec 23 20:57:26 2013
  4. loop 1 done at: Mon Dec 23 20:57:28 2013
  5. loop 0 done at: Mon Dec 23 20:57:30 2013
  6. all end: Mon Dec 23 20:57:30 2013

 7.2.1、threading模块

  我们应该避免使用thread模块,原因是它不支持守护线程。当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。有时我们并不期望这种行为,这时就引入了守护线程的概念。threading模块则支持守护线程。

mtsleep3.py

  1. #coding=utf-8
  2. import threading
  3. from time import sleep, ctime
  4.  
  5. loops = [4,2]
  6.  
  7. def loop(nloop, nsec):
  8. print 'start loop', nloop, 'at:', ctime()
  9. sleep(nsec)
  10. print 'loop', nloop, 'done at:', ctime()
  11.  
  12. def main():
  13. print 'starting at:', ctime()
  14. threads = []
  15. nloops = range(len(loops))
  16.  
  17. #创建线程
  18. for i in nloops:
  19. t = threading.Thread(target=loop,args=(i,loops[i]))
  20. threads.append(t)
  21.  
  22. #开始线程
  23. for i in nloops:
  24. threads[i].start()
  25.  
  26. #等待所有结束线程
  27. for i in nloops:
  28. threads[i].join()
  29.  
  30. print 'all end:', ctime()
  31.  
  32. if __name__ == '__main__':
  33. main()

运行结果:

  1. starting at: Mon Dec 23 22:58:55 2013
  2. start loop 0 at: Mon Dec 23 22:58:55 2013
  3. start loop 1 at: Mon Dec 23 22:58:55 2013
  4. loop 1 done at: Mon Dec 23 22:58:57 2013
  5. loop 0 done at: Mon Dec 23 22:58:59 2013
  6. all end: Mon Dec 23 22:58:59 2013

start()

  开始线程活动

join()

  等待线程终止

  所有的线程都创建了之后,再一起调用 start()函数启动,而不是创建一个启动一个。而且,不用再管理一堆锁(分配锁,获得锁,释放锁,检查锁的状态等),只要简单地对每个线程调用 join()函数就可以了。

join()会等到线程结束,或者在给了 timeout 参数的时候,等到超时为止。join()的另一个比较重要的方面是它可以完全不用调用。一旦线程启动后,就会一直运行,直到线程的函数结束,退出为止。

使用可调用的类

mtsleep4.py

  1. #coding=utf-8
  2. import threading
  3. from time import sleep, ctime
  4.  
  5. loops = [4,2]
  6.  
  7. class ThreadFunc(object):
  8.  
  9. def __init__(self,func,args,name=''):
  10. self.name=name
  11. self.func=func
  12. self.args=args
  13.  
  14. def __call__(self):
  15. apply(self.func,self.args)
  16.  
  17. def loop(nloop,nsec):
  18. print "seart loop",nloop,'at:',ctime()
  19. sleep(nsec)
  20. print 'loop',nloop,'done at:',ctime()
  21.  
  22. def main():
  23. print 'starting at:',ctime()
  24. threads=[]
  25. nloops = range(len(loops))
  26.  
  27. for i in nloops:
  28. #调用ThreadFunc实例化的对象,创建所有线程
  29. t = threading.Thread(
  30. target=ThreadFunc(loop,(i,loops[i]),loop.__name__))
  31. threads.append(t)
  32.  
  33. #开始线程
  34. for i in nloops:
  35. threads[i].start()
  36.  
  37. #等待所有结束线程
  38. for i in nloops:
  39. threads[i].join()
  40.  
  41. print 'all end:', ctime()
  42.  
  43. if __name__ == '__main__':
  44. main()

运行结果:

  1. starting at: Tue Dec 24 16:39:16 2013
  2. seart loop 0 at: Tue Dec 24 16:39:16 2013
  3. seart loop 1 at: Tue Dec 24 16:39:16 2013
  4. loop 1 done at: Tue Dec 24 16:39:18 2013
  5. loop 0 done at: Tue Dec 24 16:39:20 2013
  6. all end: Tue Dec 24 16:39:20 2013

  创建新线程的时候,Thread 对象会调用我们的ThreadFunc 对象,这时会用到一个特殊函数__call__()。由于我们已经有了要用的参数,所以就不用再传到 Thread()的构造函数中。由于我们有一个参数的元组,这时要在代码中使用 apply()函数。

我们传了一个可调用的类(的实例),而不是仅传一个函数。

__init__()

方法在类的一个对象被建立时运行。这个方法可以用来对你的对象做一些初始化。

apply()

apply(func [, args [, kwargs ]]) 函数用于当函数参数已经存在于一个元组或字典中时,间接地调用函数。args是一个包含将要提供给函数的按位置传递的参数的元组。如果省略了args,任何参数都不会被传递,kwargs是一个包含关键字参数的字典。

apply() 用法:

  1. #不带参数的方法
  2. >>> def say():
  3. print 'say in'
  4.  
  5. >>> apply(say)
  6. say in
  7.  
  8. #函数只带元组的参数
  9. >>> def say(a,b):
  10. print a,b
  11.  
  12. >>> apply(say,('hello','虫师'))
  13. hello 虫师
  14.  
  15. #函数带关键字参数
  16. >>> def say(a=1,b=2):
  17. print a,b
  18.  
  19. >>> def haha(**kw):
  20. apply(say,(),kw)
  21.  
  22. >>> haha(a='a',b='b')
  23. a b

python多线程概念的更多相关文章

  1. python 多线程概念

    ######多线程##### 什么是线程: 线程是操作系统能够进行运算调度的最小单位(程序执行流的最小单元).它被包含在进程之中,是进程中的实际运作单位. 一个进程中可以并发多个线程,每条线程并行执行 ...

  2. Python多线程锁

    [Python之旅]第六篇(四):Python多线程锁   python lock 多线程 多线程使用方法 多线程锁 摘要:   在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来 ...

  3. Day9 - Python 多线程、进程

    Python之路,Day9, 进程.线程.协程篇   本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线 ...

  4. 搞定python多线程和多进程

    1 概念梳理: 1.1 线程 1.1.1 什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发 ...

  5. 进程,线程,GIL,Python多线程,生产者消费者模型都是什么鬼

    1. 操作系统基本知识,进程,线程 CPU是计算机的核心,承担了所有的计算任务: 操作系统是计算机的管理者,它负责任务的调度.资源的分配和管理,统领整个计算机硬件:那么操作系统是如何进行任务调度的呢? ...

  6. python多线程、多进程以及GIL

    多线程 使用threading模块创建线程 传入一个函数 这种方式是最基本的,即调用threading中的Thread类的构造函数,然后指定参数target=func,再使用返回的Thread的实例调 ...

  7. 浅析Python多线程

    学习Python多线程的资料很多,吐槽Python多线程的博客也不少.本文主要介绍Python多线程实际应用,且假设读者已经了解多线程的基本概念.如果读者对进程线程概念不甚了解,可参见知名博主 阮一峰 ...

  8. day-3 python多线程编程知识点汇总

    python语言以容易入门,适合应用开发,编程简洁,第三方库多等等诸多优点,并吸引广大编程爱好者.但是也存在一个被熟知的性能瓶颈:python解释器引入GIL锁以后,多CPU场景下,也不再是并行方式运 ...

  9. Python多线程操作

    多线程是一门编程语言的重要操作. GIL(全局解释器锁)存在于python解释器中,用来确保当前只有一个线程被执行,当一个线程获得GIL后,这个线程将被执行,退出时释放GIL,由下一个获得GIL的线程 ...

随机推荐

  1. apt-get出现无法定位安装包问题解决

    这个问题出现在sources.list上 编辑/etc/apt/sources.list下的文件 找到检查你的存储库是否正确 你可以在以下页面找到不同版本 Kali Linux 的存储库:http:/ ...

  2. 设置Nginx开机自启动

    Nginx 是一个很强大的高性能Web和反向代理服务器.虽然使用命令行可以对nginx进行各种操作,比如启动等,但是还是根据不太方便.下面介绍在linux下安装后,如何设置开机自启动. 首先,在lin ...

  3. 关于多重嵌套的JSON数据解析

    最近项目中需要封装一套复杂的数据模型返回给前端,大致就是一个用户会有多笔订单,每个订单下可能会有多笔保单, 大致的数据模型如下: 为了方面描述,先看一下一个用户下有一条订单,一条订单下有一个保险订单的 ...

  4. Git_添加远程库

    现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举 ...

  5. php 获取开始日期与结束日期之间所有日期

    话不多说,源码奉上! function getDateRange($startdate, $enddate) { $stime = strtotime($startdate); $etime = st ...

  6. MVC使用Dotnet.HighCharts做图表01,区域图表

    如果想在MVC中使用图表显示的话,DotNet.HighCharts是不错的选择.DotNet.HighCharts是一个开源的JavaScript图表库,支持线型图表.柱状图标.饼状图标等几十种图标 ...

  7. merge into优化sql(转)

    使用Merge INTO优化SQL,性能提升巨大 分类: Oracle 2017-04-13 10:55:07   说说背景:开发有个需求,需要对新加的一个字段根据特定的业务逻辑更新数据.TPS_TR ...

  8. 《MacTalk·人生元编程》

    <MacTalk·人生元编程> 基本信息 <MacTalk·人生元编程> 基本信息 作者:池建强 出版社:人民邮电出版社 ISBN:9787115342232 上架时间:201 ...

  9. my.cnf 配置文件参数解释

    my.cnf 配置文件参数解释: #*** client options 相关选项 ***# #以下选项会被MySQL客户端应用读取.注意只有MySQL附带的客户端应用程序保证可以读取这段内容.如果你 ...

  10. C++:友元运算符重载函数

    运算符重载函数:实现对象之间进行算数运算,(实际上是对象的属性之间做运算),包括+(加号).-(减号).*./.=.++.--.-(负号).+(正号) 运算符重载函数分为:普通友元运算符重载函数.成员 ...