zoukankan      html  css  js  c++  java
  • python高级学习笔记

    每当要对序列中的内容进行循环处理时,就应该尝试用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.imapfunction, *iterables)与内建函数mapReturn 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对象。特别的,函数能够当作一个普通值传递给另一个函数或者从另一个函数返回得来。这比CC++里的函数指针的传递更加方便。这里举个例子,这以一个函数作为参数,并返回该函数的名字:

    >>> 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)

    instanceowner class的一个实例,ownerowner 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,在cpythonGIL是一个全局线程锁:在解释器解释执行任何 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的管理中有两个方法:joinsetDaemon
    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对象上当然也是可以调用acquirerelease方法。

    当一个线程获得条件变量后,调用这个条件变量的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]])

    如果可选参数blocktrue并且timeoutNone(缺省值),线程被block,直到队列空出一个数据单元。如果timeout大于0,在timeout的时间内,仍然没有可用的数据单元,Full exception被抛出。反之,如果block参数为false(忽略timeout参数),item被立即加入到空闲数据单元中,如果没有空闲数据单元,Full exception被抛出。

    • get([block[, timeout])

    Queueget方法是从队首取数据,参数和put方法一样。如果block参数为truetimeoutNone(缺省值),线程被block,直到队列中有数据。如果timeout大于0,在timeout时间内,仍然没有可取数据,Empty exception被抛出。反之,如果block参数为false(忽略timeout参数),队列中的数据被立即取出。如果此时没有可取数据,Empty exception也会被抛出。

    总结:

    在设计使用多线程的时候,一定要考虑死锁的情况。所以如果使用了锁、条件变量等同步机制的话,一定要注意仔细检查,防止死锁情况的发生。对于可能产生异常的临界区要使用异常处理机制中的finally子句来保证释放锁。等待一个条件变量的线程必须用notify()方法显式的唤醒,否则就永远沉默。保证每一个wait()方法调用都有一个相对应的notify()调用,当然也可以调用notifyAll()方法以防万一。

     

  • 相关阅读:
    Selenium 3 + BrowserMobProxy 2.1.4 模拟浏览器访问 (含趟坑)
    macOS Sierra WiFi connecting problem
    Accumulator<Long> implements of JavaSparkContext in Spark1.x
    写了一个Android动画的启动界面
    用C#简单实现了数据的封装
    关于JAVA数据结构中的栈操作
    写了一个关于将XML文件导入数据库的程序(C#,sql server)
    经典电影里的数学应用
    初步学习多线程操作,代码不是完美的,欢迎大牛指点(运行通过)
    写了一份统计网站(ASP.NET)日访问量的源码(保存至数据库,部分性能待优化),运行通过。
  • 原文地址:https://www.cnblogs.com/ghost240/p/2526740.html
Copyright © 2011-2022 走看看