python进阶学习(四)
在使用多线程之前,我们首页要理解什么是进程和线程。
什么是进程?
计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间。
什么是线程?
线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中,共享相同的运行环境。我们可以想像成是在主进程或“主线程”中并行运行的“迷你进程”。
7.2.1、单线程
在单线程中顺序执行两个循环。一定要一个循环结束后,另一个才能开始。总时间是各个循环运行时间之和。
onetherad.py

- from time import sleep, ctime
- def loop0():
- print 'start loop 0 at:', ctime()
- sleep(4)
- print 'loop 0 done at:', ctime()
- def loop1():
- print 'start loop 1 at:', ctime()
- sleep(2)
- print 'loop 1 done at:', ctime()
- def main():
- print 'start:', ctime()
- loop0()
- loop1()
- print 'all end:', ctime()
- if __name__ == '__main__':
- main()

运行结果:
- start loop 0 at: Mon Dec 23 09:59:44 2013
- loop 0 done at: Mon Dec 23 09:59:48 2013
- start loop 1 at: Mon Dec 23 09:59:48 2013
- loop 1 done at: Mon Dec 23 09:59:50 2013
- 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

- import thread
- from time import sleep, ctime
- loops = [4,2]
- def loop0():
- print 'start loop 0 at:', ctime()
- sleep(4)
- print 'loop 0 done at:', ctime()
- def loop1():
- print 'start loop 1 at:', ctime()
- sleep(2)
- print 'loop 1 done at:', ctime()
- def main():
- print 'start:', ctime()
- thread.start_new_thread(loop0, ())
- thread.start_new_thread(loop1, ())
- sleep(6)
- print 'all end:', ctime()
- if __name__ == '__main__':
- main()

start_new_thread()要求一定要有前两个参数。所以,就算我们想要运行的函数不要参数,我们也要传一个空的元组。
这个程序的输出与之前的输出大不相同,之前是运行了 6,7 秒,而现在则是 4 秒,是最长的循环的运行时间与其它的代码的时间总和。
运行结果:

- start: Mon Dec 23 10:05:09 2013
- start loop 0 at: Mon Dec 23 10:05:09 2013
- start loop 1 at: Mon Dec 23 10:05:09 2013
- loop 1 done at: Mon Dec 23 10:05:11 2013
- loop 0 done at: Mon Dec 23 10:05:13 2013
- 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

- #coding=utf-8
- import thread
- from time import sleep, ctime
- loops = [4,2]
- def loop(nloop, nsec, lock):
- print 'start loop', nloop, 'at:', ctime()
- sleep(nsec)
- print 'loop', nloop, 'done at:', ctime()
- #解锁
- lock.release()
- def main():
- print 'starting at:', ctime()
- locks =[]
- #以loops数组创建列表,并赋值给nloops
- nloops = range(len(loops))
- for i in nloops:
- lock = thread.allocate_lock()
- #锁定
- lock.acquire()
- #追加到locks[]数组中
- locks.append(lock)
- #执行多线程
- for i in nloops:
- thread.start_new_thread(loop,(i,loops[i],locks[i]))
- for i in nloops:
- while locks[i].locked():
- pass
- print 'all end:', ctime()
- if __name__ == '__main__':
- main()

thread.allocate_lock()
返回一个新的锁定对象。
acquire() /release()
一个原始的锁有两种状态,锁定与解锁,分别对应acquire()和release() 方法。
range()
range()函数来创建列表包含算术级数。
range(len(loops))理解:

- >>> aa= "hello"
- #长度计算
- >>> len(aa)
- 5
- #创建列表
- >>> range(len(aa))
- [0, 1, 2, 3, 4]
- #循环输出列表元素
- >>> for a in range(len(aa)):
- print a
- 0
- 1
- 2
- 3
- 4

我们先调用 thread.allocate_lock()函数创建一个锁的列表,并分别调用各个锁的 acquire()函数获得锁。获得锁表示“把锁锁上”。锁上后,我们就把锁放到锁列表 locks 中。
下一个循环创建线程,每个线程都用各自的循环号,睡眠时间和锁为参数去调用 loop()函数。为什么我们不在创建锁的循环里创建线程呢?有以下几个原因:(1) 我们想到实现线程的同步,所以要让“所有的马同时冲出栅栏”。(2) 获取锁要花一些时间,如果你的线程退出得“太快”,可能会导致还没有获得锁,线程就已经结束了的情况。
在线程结束的时候,线程要自己去做解锁操作。最后一个循环只是坐在那一直等(达到暂停主线程的目的),直到两个锁都被解锁为止才继续运行。
mtsleep2.py运行结果:

- starting at: Mon Dec 23 20:57:26 2013
- start loop start loop0 1at: at:Mon Dec 23 20:57:26 2013
- Mon Dec 23 20:57:26 2013
- loop 1 done at: Mon Dec 23 20:57:28 2013
- loop 0 done at: Mon Dec 23 20:57:30 2013
- all end: Mon Dec 23 20:57:30 2013

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

- #coding=utf-8
- import threading
- from time import sleep, ctime
- loops = [4,2]
- def loop(nloop, nsec):
- print 'start loop', nloop, 'at:', ctime()
- sleep(nsec)
- print 'loop', nloop, 'done at:', ctime()
- def main():
- print 'starting at:', ctime()
- threads = []
- nloops = range(len(loops))
- #创建线程
- for i in nloops:
- t = threading.Thread(target=loop,args=(i,loops[i]))
- threads.append(t)
- #开始线程
- for i in nloops:
- threads[i].start()
- #等待所有结束线程
- for i in nloops:
- threads[i].join()
- print 'all end:', ctime()
- if __name__ == '__main__':
- main()

运行结果:

- starting at: Mon Dec 23 22:58:55 2013
- start loop 0 at: Mon Dec 23 22:58:55 2013
- start loop 1 at: Mon Dec 23 22:58:55 2013
- loop 1 done at: Mon Dec 23 22:58:57 2013
- loop 0 done at: Mon Dec 23 22:58:59 2013
- all end: Mon Dec 23 22:58:59 2013

start()
开始线程活动
join()
等待线程终止
所有的线程都创建了之后,再一起调用 start()函数启动,而不是创建一个启动一个。而且,不用再管理一堆锁(分配锁,获得锁,释放锁,检查锁的状态等),只要简单地对每个线程调用 join()函数就可以了。
join()会等到线程结束,或者在给了 timeout 参数的时候,等到超时为止。join()的另一个比较重要的方面是它可以完全不用调用。一旦线程启动后,就会一直运行,直到线程的函数结束,退出为止。
使用可调用的类
mtsleep4.py

- #coding=utf-8
- import threading
- from time import sleep, ctime
- loops = [4,2]
- class ThreadFunc(object):
- def __init__(self,func,args,name=''):
- self.name=name
- self.func=func
- self.args=args
- def __call__(self):
- apply(self.func,self.args)
- def loop(nloop,nsec):
- print "seart loop",nloop,'at:',ctime()
- sleep(nsec)
- print 'loop',nloop,'done at:',ctime()
- def main():
- print 'starting at:',ctime()
- threads=[]
- nloops = range(len(loops))
- for i in nloops:
- #调用ThreadFunc实例化的对象,创建所有线程
- t = threading.Thread(
- target=ThreadFunc(loop,(i,loops[i]),loop.__name__))
- threads.append(t)
- #开始线程
- for i in nloops:
- threads[i].start()
- #等待所有结束线程
- for i in nloops:
- threads[i].join()
- print 'all end:', ctime()
- if __name__ == '__main__':
- main()

运行结果:

- starting at: Tue Dec 24 16:39:16 2013
- seart loop 0 at: Tue Dec 24 16:39:16 2013
- seart loop 1 at: Tue Dec 24 16:39:16 2013
- loop 1 done at: Tue Dec 24 16:39:18 2013
- loop 0 done at: Tue Dec 24 16:39:20 2013
- all end: Tue Dec 24 16:39:20 2013

创建新线程的时候,Thread 对象会调用我们的ThreadFunc 对象,这时会用到一个特殊函数__call__()。由于我们已经有了要用的参数,所以就不用再传到 Thread()的构造函数中。由于我们有一个参数的元组,这时要在代码中使用 apply()函数。
我们传了一个可调用的类(的实例),而不是仅传一个函数。
__init__()
方法在类的一个对象被建立时运行。这个方法可以用来对你的对象做一些初始化。
apply()
apply(func [, args [, kwargs ]]) 函数用于当函数参数已经存在于一个元组或字典中时,间接地调用函数。args是一个包含将要提供给函数的按位置传递的参数的元组。如果省略了args,任何参数都不会被传递,kwargs是一个包含关键字参数的字典。
apply() 用法:

- #不带参数的方法
- >>> def say():
- print 'say in'
- >>> apply(say)
- say in
- #函数只带元组的参数
- >>> def say(a,b):
- print a,b
- >>> apply(say,('hello','虫师'))
- hello 虫师
- #函数带关键字参数
- >>> def say(a=1,b=2):
- print a,b
- >>> def haha(**kw):
- apply(say,(),kw)
- >>> haha(a='a',b='b')
- a b

python进阶学习(四)的更多相关文章
- Python进阶----反射(四个方法),函数vs方法(模块types 与 instance()方法校验 ),双下方法的研究
Python进阶----反射(四个方法),函数vs方法(模块types 与 instance()方法校验 ),双下方法的研究 一丶反射 什么是反射: 反射的概念是由Smith在1982年首次提出的 ...
- Python进阶(十四)----空间角度研究类,类与类之间的关系
Python进阶(十四)----空间角度研究类,类与类之间的关系 一丶从空间角度研究类 对象操作对象属性 class A(): address = '沙河' def __init__(self, na ...
- Python进阶学习之特殊方法实例详析
Python进阶学习之特殊方法实例详析 最近在学习python,学习到了一个之前没接触过的--特殊方法. 什么是特殊方法?当我们在设计一个类的时候,python中有一个用于初始化的方法$__init_ ...
- Python基础学习四
Python基础学习四 1.内置函数 help()函数:用于查看内置函数的用途. help(abs) isinstance()函数:用于判断变量类型. isinstance(x,(int,float) ...
- python进阶学习笔记(一)
python进阶部分要学习的内容: 学习目标: 1.函数式编程 1.1,什么是函数式编程 函数式编程是一种抽象计算的编程模式 不同语言的抽象层次不同: 函数式编程的特点: python支持的函数式编程 ...
- 魔法方法推开Python进阶学习大门
热爱Python Python是Guido van Rossum设计出来的让使用者觉得如沐春风的一门编程语言.2020年11月12日,64岁的Python之父宣布由于退休生活太无聊,自己决定加入Mic ...
- python进阶学习三——第四天
一. iter&yield迭代器 1.1 iter names = iter(['zeng', 'chun', 'yun']) print(names) print(names.__next_ ...
- python进阶学习笔记(四)--多线程thread
在使用多线程之前,我们首页要理解什么是进程和线程. 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据.它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期.进程( ...
- [python]进阶学习之阅读代码
起因 最近在公司的任务是写一些简单的运营工具,因为是很小的工具,所以就用了github上面的一个开源项目flask-admin,可以省去很多的事情. 但是,这个开源项目是个人维护的项目,所以文档相对简 ...
随机推荐
- 【SignalR学习系列】7. SignalR Hubs Api 详解(JavaScript 客户端)
SignalR 的 generated proxy 服务端 public class ContosoChatHub : Hub { public void NewContosoChatMessage( ...
- http post,get,put,delete区别(收集整理)
摘要: 这篇文章想从restful角度来分析下http的方法get,post,put,delete的区别.先暂时收集下资料~~~ 一 Http 规范中的get与post 根据 HTTP 规范,GET ...
- opnet仿真过程中SEED的概念问题 分类: opnet 2014-11-02 15:25 69人阅读 评论(0) 收藏
仿真配置中SEED的概念:仿真随机种子,是产生随机数的种子值,反应随机数的状态.只要选定一个种子值,整个随机事件系统就固定了,复杂仿真的随机过程就成了一次实现.目的是测试仿真系统的稳健性,具体来说,针 ...
- Vue事件处理
前面的话 Vue事件监听的方式貌似违背了关注点分离(separation of concern)的传统理念.实际上,所有的Vue.js事件处理方法和表达式都严格绑定在当前视图的ViewModel上,它 ...
- Python爬虫从入门到放弃(二十三)之 Scrapy的中间件Downloader Middleware实现User-Agent随机切换
总架构理解Middleware 通过scrapy官网最新的架构图来理解: 这个图较之前的图顺序更加清晰,从图中我们可以看出,在spiders和ENGINE提及ENGINE和DOWNLOADER之间都可 ...
- 三菱Q系列PLC基本指令讲解
1.数据传送指令MOV和MOVP,格式为 MOV SRC1 DES1 表示条件接通,将SRC1的值传送到DES1寄存器中,带P的表示只在条件接通的上升沿指令执行一个扫描周期,不带P ...
- 教你如何反编译app,拿到加密方式
大家知道app 有安卓和ios 安卓是apk 现在基本上apk都是经过加密的 想动态脱壳没一定的技术是搞不定的 IOS是ipa 今天我主要讲的是这个 准备好反编译设备 1.一套越狱的ios手机 我 ...
- WPF中使用Data Annotations验证Model
.NET Framework中System.ComponentModel.DataAnnotations提供了很多属性来验证对象的属性.可以在C:\Program Files (x86)\Refere ...
- 安卓Service完全解析(中)
摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇中我们学习了Android Service相关的许多基础但是重要的内容,基本涵盖大部分平日里的开发工作.今天我们继续学习一下稍微高级 ...
- Linux逻辑卷管理器concept
Linux逻辑卷管理concept-------------------------转载2013/10/09 通过使用Linux的逻辑卷管理器(Logical Volume Manager, LVM) ...