每当要对序列中的内容进行循环处理时,就应该尝试用list comprehensions代替它
>>> i = iter('abc')
>>> i.next()
'a'
>>> i.next()
'b'
>>> i.next()
'c'
>>> i.next()
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
i.next()
StopIteration
当序列遍历完时,将抛出一个StopIteration异常,这将使迭代器和循环兼容,因为他们将捕获这个异常以停止循环。
生成器里面的yield会自动有一个next函数
>>> def fibonacci():
a, b = 0 ,1
while True:
yield b
a, b = b, a + b
>>> fib = fibonacci()
>>> fib.next()
1
>>> fib.next()
1
>>> fib.next()
2
>>> [fib.next() for i in range(10)]
[3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
send函数的工作机制与next一样,但是yield将变成能够返回传入的值。因为,这个函数以根据客户端代码来改变其行为
协同程序
在python中,协同程序的代替者是线程,它可以实现代码块之间的交互,但是因为他们表现出一种抢先式的风格,所以必须注意资源锁,而协同程序不需要.协同程序是用yield写的
python为了编写针对序列的简单生成器提供了一种快捷方式,可以用一种类似列表推到的语法来代替yield。在此,使用圆括号代替中括号。
>>> iter = (x**2 for x in range(10) if x % 2 == 0)
>>> for el in iter:
print el
0
4
16
36
64
itertools模块
1.islice:窗口迭代器。Islice将返回一个运行在序列的子分组之上的迭代器
>>> import itertools
>>> def starting_at_five():
value = raw_input().strip()
while value != '':
for el in itertools.islice(value.split(), 4, None): //islice第一个变量
yield el 可以是迭代
value = raw_input().strip()
>>> iter = starting_at_five()
>>> iter.next()
1 2 3 4 5 6
'5'
>>> iter.next()
'6'
>>> iter.next()
1 2
1 2
1 2 3 4
1 2 3 4 5
'5'
2.tee:返回两个迭代器。
>>> def with_head(iterable, headsize=1):
a, b = itertools.tee(iterable)
return list(itertools.islice(a, headsize)), list(b)
>>> seq = [1,2,3,4,5]
>>> with_head(seq)
([1], [1, 2, 3, 4, 5])
>>> def with_head(iterable, headsize=1):
a, b = itertools.tee(iterable)
return list(a), list(b)
>>> with_head(seq)
([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])
3.groupby:是一个行程长度编码(RLE)压缩数据。
如get uuuuuuuup被改为1g1e1t1 8u1p
>>> from itertools import groupby
>>> def compress(data):
return ((len(list(group)), name) for name, group in groupby(data))
>>> def decompress(data):
return (car * size for size, car in data)
>>> list(compress('get ttttuuuup'))
[(1, 'g'), (1, 'e'), (1, 't'), (1, ' '), (4, 't'), (4, 'u'), (1, 'p')]
4.chain(*iterables)创建一个在第一个可迭代的对象上迭代的迭代器,然后继续下一个,以此类推
5.count([n])返回一个给出连续整数的迭代器
>>> x = itertools.count(0, 2)
>>> for i in x:
if(i > 20):
break;
else:
print i
0
2
4
6
8
10
12
14
16
18
20
6.cycle(iterable)在可迭代对象的每个元素之上迭代,然后重新开始,无限次地重复
7.dropwhile(predicate, iterable)只要断言(predicate)为真,就从可迭代对象中删除每个元素。当断言为假时则输出剩余的元素
8.ifilter(predicate, iterable)近似于内建函数filter
9.ifilterfalse(同上)与ifilter类似,但是将在断言为假时执行迭代。
10.imap(function, *iterables)与内建函数map(Return a list of the results of applying the function to the items of the argument sequence(s))类似。不过它将在多个可迭代对象上工作,在最短的可迭代对象耗尽时将停止。
11.izip(*iterbles)和zip类似,不过它将返回一个迭代器
12.repeat(object[, times])返回一个迭代起,该迭代起在每次调用时返回object,运行times次,没有指定为无限次
with语句也可以自己定义自己的类来写。
>>> class Context:
def __enter__(self):
print 'entering the zone'
def __exit__(self, exception_type, exception_value, exception_tracebace):
print 'leaving the zone'
if exception_type is None:
print 'with no error'
else:
print 'with an error (%s)'
>>> with Context()
SyntaxError: invalid syntax
>>> with Context():
print 'i am the zone'
entering the zone
i am the zone
leaving the zone
with no error
>>> with Context():
print 'i am the zone'
raise TypeError('i am the bug')
entering the zone
i am the zone
leaving the zone
with an error (%s)
Traceback (most recent call last):
File "<pyshell#49>", line 3, in <module>
raise TypeError('i am the bug')
TypeError: i am the bug
contextlib模块提供了上面的模式__enter__,与__exit__
>>> from contextlib import contextmanager
>>> @contextmanager
def context():
print 'entering the zone'
try:
yield
except Exception, e:
print 'with an error (%s)' % e
raise e
else:
print 'with no error'
>>> with context():
print 'i am the zone'
raise TypeError('i am the bug')
entering the zone
i am the zone
with an error (i am the bug)
Traceback (most recent call last):
File "<pyshell#65>", line 3, in <module>
raise TypeError('i am the bug')
TypeError: i am the bug
Python修饰器的使用
关于first-class
函数在python中是first-class对象。特别的,函数能够当作一个普通值传递给另一个函数或者从另一个函数返回得来。这比C/C++里的函数指针的传递更加方便。这里举个例子,这以一个函数作为参数,并返回该函数的名字:
>>>
def show_func(f):
... print f.__name__
再举个例子以使用上面定义的函数:
>>>
def foo():
... print 'foo here'
>>>
show_func(foo)
foo
再看下面,它是一个函数,它创建一个新函数然后把它当作結果返回。在这种情况下,make_adder()创建了一个函数,这个函数的作用只是将参量加上一个常量。
>>>
def make_adder(x):
... def adder(y):
...
return x+y
... return
adder
>>>
a=make_adder(5)
>>> a(10)
15
其实感觉make_adder()就像一个函数工厂,专门负责产生函数。
包装函数
把上面的例子好好想想,你会得到一个不错的想法,我们可以创建一类特别的函数,它们接受一个函数作为参数,并且返回一个新的函数,返回的函数与输入的参数没有关系,那么我们就完全改变了原来的函数的用途,而如果返回的函数就是传入的函数,那么其实我们什么也没有做。不过,大多数情况下,我们对某些功能函数有特别的要求,所以只是对原来的函数作一些小的动作,然后返回一个功能大致相似的函数。
举个例子吧,我们包装一个函数,使得对它的调用被记录到日志文件中去:
>>>
def show_call(f):
... def shown(*args,
**kwargs):
... print
'Calling', f.__name__
... return
f(*args, **kwargs)
... return shown
>>>
bar = show_call(foo)
>>> bar()
Calling foo
foo here
如果我们将show_foo()返回的值再次与传参名字进行绑定,那么我们很有效率地取得了一个原来函数的包装后版本:
>>>
foo = show_call(foo)
>>> foo()
Calling foo
foo here
修饰器是python语法上的甜味剂
如果我们继续上面的讨论,其实你就已经理解了修饰器的核心本质,因为一个修饰器就是python语法上的甜味剂,正如上面写的例子那样,只是为美中不足做些弥补。不过,有简单语法可供使用:
@show_call
def
foo():
pass
它就等同于下面的代码:
def
foo():
pass
foo = show_call(foo)
任何函数,只要它接受一个函数参数,并返回一个函数,都能被当成修饰器来用。
这是一个大创新吗?
事实上,这没有什么大不了的。修饰器并没有添加什么新的函数到python库中。它只是借用一种新的语法形式来实现一个古老的想法。但是修饰器是语法上的一大变革,它非常流行,而且广泛运用在现代的python代码中。
修饰器非常有用,它被广泛运用在需要代理一些函数实现各种不同功能的场合。函数前的修饰器,以一种独特的语法,使函数意图和功能更加明确。
表现良好的修饰器
当有一个修饰器显示在函数上头时,就表示原始函数与修饰之后的函数有了明显的不同:
>>>
def bar():
... ''' function bar() '''
...
pass
>>>
bar.__name__, bar.__doc__, bar.__module__
('bar','function
bar()','__main__')
>>>
import inspect
>>> inspect.getargspec(bar)
([], None,
None, None)
>>>
bar2=show_call(bar)
>>> bar2.__name__, bar2.__doc__,
bar2.__module__
('shown', None, '__main__')
>>>
inspect.getargspec(bar2)
([],'args','kwargs',None)
从上面的例子可以看到,函数属性并没有拷贝给包装之后的函数,原函数的属性可以通过拷贝的方法,保留给包装后的函数。下面是一个更好的show_call():
def
show_call(f):
def shown(*args, **kwds):
print 'Calling', f.__name__
return f(*args, **kwds)
shown.__name__
= f.__name__
shown.__doc__ = f.__doc__
shown.__module__ = f.__module__
shown.__dict__.update(f.__dict__)
return
shown
改进后的版本,尽管属性正确了,但是其署名还是错误:
>>>
bar2=show_call(bar)
>>> bar2.__name__, bar2.__doc__,
bar2.__module__
('bar', ' Function bar() ', '__main__')
python2.5中的functools模块避免了你写上面冗长而无聊的代码,它提供了一个修饰器的修饰器,名叫wrap()。上面的例子可以写成如下形式:
>>>
def show_call(f):
... @wraps(f)
... def
shown(*args, **kwds):
... print
'Calling', f.__name__
... return
f(*args, **kwds)
... return shown
>>>
bar2=show_call(bar)
>>> bar2.__name__, bar2.__doc__,
bar2.__module__
('bar', ' Function bar() ', '__main__')
带参数的修饰器
你可能注意到上面那个所谓的修饰器的修饰器很奇怪,因为它居然接受一个参数。那它是怎么工作的呢?
想像我们有这样一段代码:
@wraps(f)
def
do_nothing(*args, **kwds):
return f(*args, **kwds)
它实际等价于:
def
do_nothing(*args, **kwds):
return f(*args,
**kwds)
do_nothing = wraps(f)(do_nothing)
为了使上面的属性保留有意义,wraps()必须是一个修饰器工厂,这个工厂返回的值本身就是一个修饰器。很绕吧!总之,wraps()是一个返回值为函数的函数,返回的函数是一个以函数为参数并返回函数的函数。呵呵。
举个简单的例子吧。我们希望一个修饰器反复调用它所修饰的函数。对于一个固定的调用次数,可以写成如下:
>>>
def repeat3(f):
... def inner(*args, **kwds):
...
f(*args, **kwds)
... f(*args,
**kwds)
... return f(*args, **kwds)
...
return inner
>>> f3=repeat3(foo)
>>> f3()
foo
here
foo here
foo here
但是我们想传递一个参数控制反复调用的次数。这样我们需一个函数它的返回值是一个修饰器。这个返回的修饰器将与上面的repeat3()很相似。它需要另一层包装:
>>>
def repeat(n):
... def repeatn(f):
... def
inner(*args, **kwds):
... for i in
range(n):
... ret = f(*args,
**kwds)
... return ret
...
return inner
... return repeatn
这里的repeat()就是一个修饰器工厂,repeatn()是实际上是一个修饰器,而inner()是被调用的包装函数。下面是使用这个修饰器的语法:
>>>
@repeat(4)
... def bar():
... print 'bar here'
>>>
bar()
bar here
bar here
bar here
bar here
>>> class distinctdict(dict):
def __setitem__(self, key, value):
try:
value_index = self.values().index(value)
existing_key = self.keys()[value_index]
if existing_key != key:
raise DistinctError(("This value exixts"))
except ValueError:
pass
else:
super(distinctdict, self).__setitem__(key,value)
>>> a = distinctdict()
>>> a[1] = 1
>>> a[1] = 2
>>> a[2] = 2
>>> a.items()
[]
>>> a[1] = 1
>>> a.items()
[]
>>> class distinctdict(dict): 区分与上面的区别
def __setitem__(self, key, value):
try:
value_index = self.values().index(value)
existing_key = self.keys()[value_index]
if existing_key != key:
raise DistinctError(("This value exixts"))
except ValueError:
pass
super(distinctdict, self).__setitem__(key,value)
>>> a = distinctdict()
>>> a[1] = 1
>>> a[2] = 2
>>> a.items()
[(1, 1), (2, 2)]
super的缺陷P76(高级)
>>> class B:
__base_num = 0
>>> class D(B):
pass
>>> b = B()
>>> b._B__base_num
0
>>> d = D()
>>> d._B__base_num
0
>>> d._D__base_num
>>> class B:
_b = 0
__b = 1
>>> B.__dict__
{'__module__': '__main__', '_B__b': 1, '_b': 0, '__doc__': None}
>>> dir(B)
['_B__b', '__doc__', '__module__', '_b']
>>> b = B()
>>> dir(b)
['_B__b', '__doc__', '__module__', '_b']
>>> b.__dict__
{}
python中的描述符
一般的得到一个类或是一个实例的属性的方法是从类或是实例的__dict__中查找。这样属性
可以做的事情会因为这种方式而得到限制(他们仅仅是一个data,或是第一个默认参数是所属
实例的方法)。为了提供更大的弹性,就有了描述符descriptors。
在这种体制中,主类(owner class)有方法是描述符定义的。
他们定义了三个接口:
__get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)
instance是owner class的一个实例,owner是owner class名。
如果实现了__get__和__set__就是一个data描述符,如果只有__get__就是一个non-data
描述符。不同的效果在于data描述符总是替代在一个实例中的实现,而non-data描述符可以
在实例中改变。当然如果仅仅在__set__中raise AttributeError,仍然得到的是一个
non-data的描述符。
几个python中自建的方法的利用描述符体制的python实现。
property:
class
Property(object):
def __init__(self, get=None,
set=None, del=None, doc=None):
self.get = get
self.set = set
self.del = del
self.__doc__ = doc
def __get__(self, obj,
klass=None):
if obj is
None:
return self
if
self.get is None:
raise AttributeError
return self.get(obj)
def __set__(self, obj,
value):
if self.set is
None:
raise AttributeError
self.set(obj, value)
def __delete__(self,
obj):
if self.del is
None:
raise AttributeError
self.del(obj)
staticmethod:
class
StaticMethod(object):
def __init__(self,
f):
self.f = f
def __get__(self, obj, klass=None):
return self.f
classmethod:
class
ClassMethod(object):
def __init__(self,
f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def
newfunc(*args):
return self.f(klass, *args)
return newfunc
property简化了描述符的编写,但是有点问题在于继承如:
>>> class FirstClass:
def _get_price(self):
return '$ 500'
price = property(_get_price)
>>> class SecondClass(FirstClass):
def _get_price(self):
return '$ 20'
>>> plane_ticket = SecondClass()
>>> plane_ticket.price
'$ 500'
>>> plane_ticket._get_price()
'$ 20'
解决不办法
>>> class FirstClass:
def _get_price(self):
return '$ 500'
price = property(_get_price)
>>> class SecondClass(FirstClass):
def _cheap_price(self):
return '$ 20'
>>> plane_ticket = SecondClass()
>>> plane_ticket.price
'$ 20'
槽(代替了__dict__,节省内存)
>>> class B(object):
__slots__ = ['ice', 'cream']
>>> b = B()
>>> dir(b)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'cream', 'ice']
>>> b.__dict__
Traceback (most recent call last):
File "<pyshell#202>", line 1, in <module>
b.__dict__
AttributeError: 'B' object has no attribute '__dict__'
>>> B.__dict__
<dictproxy object at 0x9d2d194>
>>> b.a = 1
Traceback (most recent call last):
File "<pyshell#204>", line 1, in <module>
b.a = 1
AttributeError: 'B' object has no attribute 'a'
>>> b.ice = 2
>>> b.ice
>>> class B:
def __init__(self):
print 'B init called'
>>> class D(B):
pass
>>> d = D()
B init called
>>> class D(B):
def __init__(self):
print 'D init called'
>>> d = D()
D init called
>>> class B(object):
def __new__(cls):
print 'new called'
return object.__new__(cls)
def __init__(self):
print 'B init called'
>>> class D(B):
def __init__(self):
print 'D init called'
>>> d = D()
new called
D init called
>>> class B(object): __new__(cls)
def __new__(cls):
print 'new called'
return object.__new__(cls)
def __init__(self):
print 'B init called'
>>> class D(B):
def __init__(self):
print 'D init called'
>>> d = D()
new called
D init called
python多线程学习笔记
预备知识
一直想了解下python的多线程,提到线程难免比较进程:
进程和线程都是操作系统控制程序运行的基本单位,系统利用此功能实现对应用来说的并发性。进程和线程的主要区别如下
-
一个程序至少一个进程,一个进程至少含有一个线程
-
进程具有独立的内存空间,多线程则共享内存空间
-
进程和线程具有不同的操作系统资源管理方式,进程有独立的地址空间。一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是一个进程中的不同执行路径,线程有堆栈和局部变量,但是线程之间没有单独的地址空间,所以一个线程死掉就等于整个进程死掉
对应python的多线程,存在一个问题就是GIL,在cpython,GIL是一个全局线程锁:在解释器解释执行任何 Python 代码时,都需要先获得这把锁才行,解释器在转到 I/O 操作时会释放这把锁。如果是运行的程序没有I/O操作,解释器会每隔100次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过sys.setcheckinterval来调整)。所以,虽然 CPython 的线程库直接封装操作系统的原生线程,但 CPython 进程做为一个整体,同一时间只有一个线程运行于解释器中,同一时间只会有一个获得了 GIL 的线程在跑,其它的线程都处于等待状态等着 GIL 的释放。多线程只是做着分时切换.
线程概念
(1)线程状态
(2)线程锁
线程的好处是开启多任务,在宏观上来看像是同时执行的。但是多线程带来的一个问题便是线程内数据共享的问题,因为多线程是在一个进程下,共享同一片内存空间。那么在多线程的运行过程中,难免出现数据共享带来的一系列问题,这便引入了锁的概念
锁有两种状态,锁定和未锁定,当一个线程需要访问一个公共变量的时候,首先应该获得锁,当锁被其他线程占用时,线程便进入同步阻塞状态,待其他的线程释放锁时,线程便可得到相应的执行。
(3)线程通信
对于图形界面编程,更好理解线程通信的好处,比如如何让一个线程运行,可以通过触发一个事件,然后通知此线程即可。这便引入了条件变量的概念,当条件满足的时候,通过触发一个信号量,线程便可得到执行的机会。
(4)线程的运行和阻塞
概况起来线程有三种阻塞状态:
-
同步阻塞
代表的是线程处于竞争锁的状态;
-
等待阻塞
代表的是线程处于被通知的状态,线程获得条件锁定后,调用“等待”将进入这个状态,一旦其他线程发出通知,线程将进入同步阻塞状态,再次竞争条件锁定;
-
其他阻塞
例如调用sleep(),join()或者iO等待,这种状态下线程不会释放获得的锁;
threading库学习
(1)基础说明
在python中使用线程通常有两种方式,函数式或者用类来包装线程对象,我通常使用后者【使用treading.Thread】
简单举例如下:
import threading import time class threadSimple(threading.Thread): def__init__(self,interval,threadName): super(threadSimple,self).__init__(name=threadName) # threading.Thread.__init__(self) self.interval=interval self.thread_stop=False def run(self): whilenot self.thread_stop: print"Current Thread Object(%s),Time:%s\n"%(self.getName(),time.ctime()) time.sleep(self.interval) def stop(self): self.thread_stop=True def main(): thread1=threadSimple(2,"A") thread2=threadSimple(2,"B") thread1.start() thread2.start() time.sleep(30) thread1.stop() thread2.stop() return if __name__=='__main__': main()
threading.Thread类的使用说明
-
run()通常根据自己的业务需要重写
-
getName()获得线程对象的名称
-
setName()设置线程对象的名称
-
start()启动线程
-
join([timeout])阻塞当前上下文线程,等待调用此方法的线程结束后再往下运行
-
setDaemon(bool),设置子线程是否跟主线程一起结束,必须在start之前调用,默认是False
-
isAlive()检查线程是否在运行中,此处的正在运行指的是启动后,终止前
注意:
python对于thread的管理中有两个方法:join和setDaemon join:如在一个线程B中调用threadA.join(),则threadA结束后,线程B才会接着threadA.join()往后运行。 setDaemon:主线程A启动了子线程B,调用B.setDaemaon(True),则主线程结束时,会把子线程B也杀死,与C/C++中得默认效果是一样的
join使用的简单举例如下:
# encoding: UTF-8 import threading import time import sys def testA(joinX): print'Begining testA' joinX.start() joinX.join() print'Logout testA' def testB(): print'Begining testB.' time.sleep(2) print'Logout testB' testJoinB = threading.Thread(target=testB) testJoinA = threading.Thread(target=testA, args=(testJoinB,)) testJoinA.start()
执行的结果是:
Begining testA Begining testB. Logout testB Logout testA
(2)锁的使用
在threading中,定义两种类型的锁,thread.Lock,thread.RLock,两个类细微的差别是在RLock允许在同一个线程多次acquire,而Lock不能。当使用RLock的时候,出现多少次acquire就要出现对应次数的release.请看如下的2个例子:
import threading lock = threading.Lock() #new Lock Object lock.acquire() lock.acquire() #deadlock lock.release() lock.release()
import threading rLock = threading.RLock() #new RLock Object rLock.acquire() rLock.acquire() #in the current thread ,the programme is ok rLock.release() rLock.release()
如上两个锁,拥有主要的实例方法:
-
acquire([timeout])使线程进入同步阻塞状态,努力尝试获得锁定
-
release()释放锁,调用此方法之前,程序必须获得锁,不然会抛异常
简单举例如下:
import threading count=0 mylock=threading.RLock() class RLockSimple(threading.Thread): def__init__(self,name): super(RLockSimple,self).__init__() self.thread_name=name def run(self): global count while True: mylock.acquire() print"\nThread(%s)locked,Count:%d"%(self.thread_name,count) if count>=6: mylock.release() print"\n Thread(%s)release,Count:%d"%(self.thread_name,count) break count+=1 print"\n Thread(%s)release,Count:%d"%(self.thread_name,count) mylock.release() def main(): thread1=RLockSimple("M") thread2=RLockSimple("P") thread1.start() thread2.start() if __name__=='__main__': main()
(3)条件锁
condition可以当做一个高级的锁,它提供更加高级的线程同步问题,thread.Condition在内部维护一把锁,默认是RLock,在Condition对象上当然也是可以调用acquire和release方法。
当一个线程获得条件变量后,调用这个条件变量的wait方法会导致此线程释放锁,并且此线程进入等待阻塞状态,直到另一个线程调用notify方法来唤醒那个进入等待阻塞的线程,如果是调用notifyAll方法会唤醒所有等待阻塞的线程。
条件变量的常用方法如下:
-
acquire([timeout])/release()调用获得锁的关联方法
-
wait([timeout])调用这个方法将使线程进入条件等待池中,等待通知,并且释放锁,使用前当前线程必须获得锁,不然将抛运行时异常
-
notify()调用这个方法将从条件等待池中挑选一个线程通知,收到通知的线程将自动调用acquire方法尝试获得锁定,其他线程仍然待在线程池中,调用此方法不会释放锁定,使用前线程必须获得锁定
-
notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
经典的举例,生产者/消费者模式:
# encoding: UTF-8 import threading import time import sys product = None con = threading.Condition() class Product(threading.Thread): def__init__(self,t_name): super(Product,self).__init__(name=t_name) def run(self): global product if con.acquire(): while True: if product is None: print'produce...' product = 'tmp' con.notify() con.wait() time.sleep(1) class Consumer(threading.Thread): def__init__(self,t_name): super(Consumer,self).__init__(name=t_name) def run(self): global product if con.acquire(): while True: if product isnot None: print'consume...' product = None con.notify() con.wait() time.sleep(1) def main(): t1 = Product("product") t2 = Consumer("consumer") t1.setDaemon(True) t2.setDaemon(True) t2.start() t1.start() time.sleep(20) sys.exit(1) if __name__=='__main__': main()
如上生产者和消费者也可以用同步队列实现:
from Queue import Queue import random import threading import time import sys class Producer(threading.Thread): def__init__(self, t_name, queue): super(Producer,self).__init__(name=t_name) self.data=queue def run(self): while True: print"%s begin product"%self.getName() self.data.put(random.random()) time.sleep(2) class Consumer(threading.Thread): def__init__(self, t_name, queue): super(Consumer,self).__init__(name=t_name) self.data=queue def run(self): while True: print"%s begin consumer"%self.getName() self.data.get() def main(): queue = Queue() producer = Producer('product thread', queue) consumer = Consumer('consumer thread', queue) consumer.setDaemon(True) producer.setDaemon(True) producer.start() consumer.start() time.sleep(20) sys.exit(1) if __name__ == '__main__': main()
当主线程调用了queue.join()阻塞,当工作线程发现队列为空时,各种的run方法返回,所有工作线程结束,同时主线程的阻塞打开,全部程序结束,简单代码如下:
from Queue import Queue import threading q = Queue() class workThread(threading.Thread): def__init__(self,num): super(workThread,self).__init__() self.num=num def run(self): while True: item = q.get() print"thread %d is doing work\n"%self.num q.task_done() def main(): for i in range(5): t=workThread(i) t.setDaemon(True) t.start() if __name__=='__main__': main() for item in range(20): q.put(item) q.join() # block until all tasks are done
Queue模块实现了一个支持多个生产者和多个消费者的FIFO队列。当共享信息需要在多线程间安全共享时,可以考虑使用Queue。
Queue两个主要的方法是:
-
put( item[, block[, timeout]])
如果可选参数block为true并且timeout为None(缺省值),线程被block,直到队列空出一个数据单元。如果timeout大于0,在timeout的时间内,仍然没有可用的数据单元,Full exception被抛出。反之,如果block参数为false(忽略timeout参数),item被立即加入到空闲数据单元中,如果没有空闲数据单元,Full exception被抛出。
-
get([block[, timeout])
Queue的get方法是从队首取数据,参数和put方法一样。如果block参数为true且timeout为None(缺省值),线程被block,直到队列中有数据。如果timeout大于0,在timeout时间内,仍然没有可取数据,Empty exception被抛出。反之,如果block参数为false(忽略timeout参数),队列中的数据被立即取出。如果此时没有可取数据,Empty exception也会被抛出。
总结:
在设计使用多线程的时候,一定要考虑死锁的情况。所以如果使用了锁、条件变量等同步机制的话,一定要注意仔细检查,防止死锁情况的发生。对于可能产生异常的临界区要使用异常处理机制中的finally子句来保证释放锁。等待一个条件变量的线程必须用notify()方法显式的唤醒,否则就永远沉默。保证每一个wait()方法调用都有一个相对应的notify()调用,当然也可以调用notifyAll()方法以防万一。