zoukankan      html  css  js  c++  java
  • Python全栈之路-Day40

    1 互斥锁(同步锁)

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    import time
    import threading
    
    def addNum():
        global num     # 在每个线程中都获取这个全局变量
        Lock.acquire() # 每次只能有一个线程在运行Lock块的代码
        temp = num
        time.sleep(0.01)
        num = temp - 1    # 对此公共变量进行-1操作
        Lock.release()
    
    num = 100  # 设定一个共享变量
    
    Lock = threading.Lock()  # 定义同步锁
    thread_list = []
    
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('Result: ', num)
    

    2 死锁与递归锁

    2.1 死锁

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    
    import threading
    import time
    
    mutexA = threading.Lock()
    mutexB = threading.Lock()
    
    class MyThread(threading.Thread):
    
        def __init__(self):
            threading.Thread.__init__(self)
    
        def run(self):
            self.fun1()
            self.fun2()
    
        def fun1(self):
    
            mutexA.acquire()  # 如果锁被占用,则阻塞在这里,等待锁的释放
    
            print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
    
            mutexB.acquire()
            print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
            mutexB.release()
    
            mutexA.release()
    
    
        def fun2(self):
    
            mutexB.acquire()
            print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
            time.sleep(0.2)
    
            mutexA.acquire()
            print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
            mutexA.release()
    
            mutexB.release()
    
    if __name__ == "__main__":
    
        print("start---------------------------%s"%time.time())
    
        for i in range(0, 10):
            my_thread = MyThread()
            my_thread.start()
    
    

    2.2 递归锁

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    
    import threading
    import time
    
    RLock = threading.RLock()  # 获得递归锁,可以acquire多次
    
    class MyThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
    
        def run(self):
            self.fun1()
            self.fun2()
    
        def fun1(self):
            RLock.acquire()  # acquire计数加1
            print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
            RLock.acquire()  # acquire计数加1
            print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
            RLock.release()  # acquire计数减1
            RLock.release()  # acquire计数减1  计数为0后其他线程就可以竞争这把锁
    
        def fun2(self):
            RLock.acquire()
            print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
            time.sleep(0.2)
            RLock.acquire()
            print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
            RLock.release()
            RLock.release()
    
    if __name__ == "__main__":
        print("start---------------------------%s"%time.time())
        for i in range(0, 10):
            my_thread = MyThread()
            my_thread.start()
    
    

    3 event对象

    线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就 会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    import threading
    import time
    import logging
    
    logging.basicConfig(level=logging.DEBUG, format='(%(threadName)-10s) %(message)s',)
    
    def worker(event):
        logging.debug('Waiting for redis ready...')
        event.wait()
        logging.debug('redis ready, and connect to redis server and do some work [%s]', time.ctime())
        time.sleep(1)
    
    def main():
        readis_ready = threading.Event()  # 完成线程间的通信 相当于标志位
        t1 = threading.Thread(target=worker, args=(readis_ready,), name='t1')
        t1.start()
    
        t2 = threading.Thread(target=worker, args=(readis_ready,), name='t2')
        t2.start()
    
        logging.debug('first of all, check redis server, make sure it is OK, and then trigger the redis ready event')
        time.sleep(3) # simulate the check progress
        readis_ready.set()
    
    if __name__=="__main__":
        main()
    
    # event.isSet():返回event的状态值;
    # event.wait():如果 event.isSet()==False将阻塞线程;
    # event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    # event.clear():恢复event的状态值为False。
    

    4 进程对象Process

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    from multiprocessing import Process
    import os
    import time
    def info(name):
    
    
        print("name:",name)
        print('parent process:', os.getppid())
        print('process id:', os.getpid())
        print("------------------")
        time.sleep(1)
    
    def foo(name):
    
        info(name)
    
    if __name__ == '__main__':
    
        info('main process line')
    
    
        p1 = Process(target=info, args=('alvin',))
        p2 = Process(target=foo, args=('egon',))
        p1.start()
        p2.start()
    
        p1.join()
        p2.join()
    
        print("ending")
    

    5 协程简介

    5.1 yield与协程

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    import time
    
    """
    传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
    如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高。
    """
    # 注意到consumer函数是一个generator(生成器):
    # 任何包含yield关键字的函数都会自动成为生成器(generator)对象
    
    def consumer():
        r = ''
        while True:
            # 3、consumer通过yield拿到消息,处理,又通过yield把结果传回;
            #    yield指令具有return关键字的作用。然后函数的堆栈会自动冻结(freeze)在这一行。
            #    当函数调用者的下一次利用next()或generator.send()或for-in来再次调用该函数时,
            #    就会从yield代码的下一行开始,继续执行,再返回下一次迭代结果。通过这种方式,迭代器可以实现无限序列和惰性求值。
            n = yield r
            if not n:
                return
            print('[CONSUMER] ←← Consuming %s...' % n)
            time.sleep(1)
            r = '200 OK'
    def produce(c):
        # 1、首先调用c.next()启动生成器
        next(c)
        n = 0
        while n < 5:
            n = n + 1
            print('[PRODUCER] →→ Producing %s...' % n)
            # 2、然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
            cr = c.send(n)
            # 4、produce拿到consumer处理的结果,继续生产下一条消息;
            print('[PRODUCER] Consumer return: %s' % cr)
        # 5、produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
        c.close()
    if __name__=='__main__':
        # 6、整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
        c = consumer()
        produce(c)
    
    
    '''
    result:
    
    [PRODUCER] →→ Producing 1...
    [CONSUMER] ←← Consuming 1...
    [PRODUCER] Consumer return: 200 OK
    [PRODUCER] →→ Producing 2...
    [CONSUMER] ←← Consuming 2...
    [PRODUCER] Consumer return: 200 OK
    [PRODUCER] →→ Producing 3...
    [CONSUMER] ←← Consuming 3...
    [PRODUCER] Consumer return: 200 OK
    [PRODUCER] →→ Producing 4...
    [CONSUMER] ←← Consuming 4...
    [PRODUCER] Consumer return: 200 OK
    [PRODUCER] →→ Producing 5...
    [CONSUMER] ←← Consuming 5...
    [PRODUCER] Consumer return: 200 OK
    '''
    

    5.2 greentlet

    greelet机制的主要思想是:生成器函数或者协程函数中的yield语句挂起函数的执行,直到稍后使用next()或send()操作进行恢复为止。可以使用一个调度器循环在一组生成器函数之间协作多个任务。greentlet是python中实现我们所谓的"Coroutine(协程)"的一个基础库.

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    from greenlet import greenlet
    
    def test1():
        print (12)
        gr2.switch()
        print (34)
        gr2.switch()
    
    def test2():
        print (56)
        gr1.switch()
        print (78)
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()
    
    

    5.3 gevent

    Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。

    gevent是第三方库,通过greenlet实现协程,其基本思想是:

    当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

    由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    
    import gevent
    import time
    
    def foo():
        print("running in foo")
        gevent.sleep(2)
        print("switch to foo again")
    
    def bar():
        print("switch to bar")
        gevent.sleep(5)
        print("switch to bar again")
    
    start=time.time()
    
    gevent.joinall(
        [gevent.spawn(foo),
        gevent.spawn(bar)]
    )
    
    print(time.time()-start)
    

    当然,实际代码里,我们不会用gevent.sleep()去切换协程,而是在执行到IO操作时,gevent自动切换,代码如下:

    #!/usr/bin/env python
    # __Author__: "wanyongzhen"
    # Date: 2017/5/9
    
    from gevent import monkey
    monkey.patch_all()
    import gevent
    from urllib import request
    import time
    
    import ssl
    ssl._create_default_https_context = ssl._create_unverified_context # 解决Mac上的报错
    def f(url):
        print('GET: %s' % url)
        resp = request.urlopen(url)
        data = resp.read()
        print('%d bytes received from %s.' % (len(data), url))
    
    start=time.time()
    
    gevent.joinall([
            gevent.spawn(f, 'https://itk.org/'),
            gevent.spawn(f, 'https://www.github.com/'),
            gevent.spawn(f, 'https://zhihu.com/'),
    ])
    
    # f('https://itk.org/')
    # f('https://www.github.com/')
    # f('https://zhihu.com/')
    
    print(time.time()-start)
    
  • 相关阅读:
    java.lang.OutOfMemoryError: bitmap size exceeds VM budget
    安装 android apk 提示应用程序未安装
    android Dialog去掉标题栏 和边框
    sqlite 中判断插入还是修改 REPLACE
    JAVA unicode转换成中文
    android listview button和EditText
    activity 接回返回值
    ActivityGroup 单击meun键没反应
    android.os.NetworkOnMainThreadException 异常处理
    android EditText或AutoCompleteTextView setOnKey事件问题
  • 原文地址:https://www.cnblogs.com/wanyuetian/p/6831372.html
Copyright © 2011-2022 走看看