• 线程进程系列*(Python)


    <python的线程与threading模块>

    一 ,线程的两种调用方式

          threading 模块建立在thread 模块之上。thread模块以低级、原始的方式来处理和控制线程,而threading 模块通过对thread进行二次封装,

    提供了更方便的api来处理线程。

    直接调用:

     1 import threading
     2 import time
     3  
     4 def sayhi(num): #定义每个线程要运行的函数
     5  
     6     print("running on number:%s" %num)
     7  
     8     time.sleep(3)
     9  
    10 if __name__ == '__main__':
    11  
    12     t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
    13     t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
    14  
    15     t1.start() #启动线程
    16     t2.start() #启动另一个线程
    17  
    18     print(t1.getName()) #获取线程名
    19     print(t2.getName())

    继承式调用:

     1 import threading
     2 import time
     3 
     4 
     5 class MyThread(threading.Thread):
     6     def __init__(self,num):
     7         threading.Thread.__init__(self)
     8         self.num = num
     9 
    10     def run(self):#定义每个线程要运行的函数
    11 
    12         print("running on number:%s" %self.num)
    13 
    14         time.sleep(3)
    15 
    16 if __name__ == '__main__':
    17 
    18     t1 = MyThread(1)
    19     t2 = MyThread(2)
    20     t1.start()
    21     t2.start()
    22     
    23     print("ending......")

    二 ,threading.thread的实例方法

    join&Daemon方法:

     1 import threading
     2 from time import ctime,sleep
     3 import time
     4 
     5 def ListenMusic(name):
     6 
     7         print ("Begin listening to %s. %s" %(name,ctime()))
     8         sleep(3)
     9         print("end listening %s"%ctime())
    10 
    11 def RecordBlog(title):
    12 
    13         print ("Begin recording the %s! %s" %(title,ctime()))
    14         sleep(5)
    15         print('end recording %s'%ctime())
    16 
    17 
    18 threads = []
    19 
    20 
    21 t1 = threading.Thread(target=ListenMusic,args=('水手',))
    22 t2 = threading.Thread(target=RecordBlog,args=('python线程',))
    23 
    24 threads.append(t1)
    25 threads.append(t2)
    26 
    27 if __name__ == '__main__':
    28 
    29     for t in threads:
    30         #t.setDaemon(True) #注意:一定在start之前设置
    31         t.start()
    32         # t.join()
    33     # t1.join()
    34     t1.setDaemon(True)
    35 
    36     #t2.join()########考虑这三种join位置下的结果?
    37     print ("all over %s" %ctime())

    join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

    setDaemon(True):

             将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。

             当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成

             想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程

             完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦

    其它方法:

     1 # run():  线程被cpu调度后自动执行线程对象的run方法
     2 # start():启动线程活动。
     3 # isAlive(): 返回线程是否活动的。
     4 # getName(): 返回线程名。
     5 # setName(): 设置线程名。
     6 
     7 threading模块提供的一些方法:
     8 # threading.currentThread(): 返回当前的线程变量。
     9 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    10 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

     

    三, 同步锁(Lock)

     1 # Author : Kelvin
     2 # Date : 2019/2/10 11:46
     3 
     4 import threading, time
     5 
     6 count = 100
     7 R = threading.Lock()
     8 
     9 
    10 def sub():
    11     global count
    12     R.acquire()  # 获取锁,锁之间串行执行
    13     temp = count
    14     time.sleep(0.01)  # 让当前线程阻塞
    15     temp -= 1
    16     count = temp
    17     R.release()  # 释放锁
    18 
    19 
    20 threading_list = []
    21 for i in range(100):
    22     t = threading.Thread(target=sub)
    23     t.start()
    24     threading_list.append(t)
    25 for i in threading_list:
    26     i.join()
    27 
    28 print(count)

    观察:time.sleep(0.1)  /0.001/0.0000001 结果分别是多少?

    多个线程都在同时操作同一个共享资源,所以造成了资源破坏,怎么办呢?(join会造成串行,失去所线程的意义)

    我们可以通过同步锁来解决这种问题

     1 R=threading.Lock()
     2  
     3 ####
     4 def sub():
     5     global num
     6     R.acquire()
     7     temp=num-1
     8     time.sleep(0.1)
     9     num=temp
    10     R.release()

    四, 线程死锁和递归锁 

    在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。下面是一个死锁的例子:

     1 import threading,time
     2 
     3 class myThread(threading.Thread):
     4     def doA(self):
     5         lockA.acquire()
     6         print(self.name,"gotlockA",time.ctime())
     7         time.sleep(3)
     8         lockB.acquire()
     9         print(self.name,"gotlockB",time.ctime())
    10         lockB.release()
    11         lockA.release()
    12 
    13     def doB(self):
    14         lockB.acquire()
    15         print(self.name,"gotlockB",time.ctime())
    16         time.sleep(2)
    17         lockA.acquire()
    18         print(self.name,"gotlockA",time.ctime())
    19         lockA.release()
    20         lockB.release()
    21 
    22     def run(self):
    23         self.doA()
    24         self.doB()
    25 if __name__=="__main__":
    26 
    27     lockA=threading.Lock()
    28     lockB=threading.Lock()
    29     threads=[]
    30     for i in range(5):
    31         threads.append(myThread())
    32     for t in threads:
    33         t.start()
    34     for t in threads:
    35         t.join()#等待线程结束,后面再讲。

    解决办法:使用递归锁,将

    1 lockA=threading.Lock()
    2 lockB=threading.Lock()<br>#--------------<br>lock=threading.RLock()

    为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

    应用

     1 import time
     2 
     3 import threading
     4 
     5 class Account:
     6     def __init__(self, _id, balance):
     7         self.id = _id
     8         self.balance = balance
     9         self.lock = threading.RLock()
    10 
    11     def withdraw(self, amount):
    12 
    13         with self.lock:
    14             self.balance -= amount
    15 
    16     def deposit(self, amount):
    17         with self.lock:
    18             self.balance += amount
    19 
    20 
    21     def drawcash(self, amount):#lock.acquire中嵌套lock.acquire的场景
    22 
    23         with self.lock:
    24             interest=0.05
    25             count=amount+amount*interest
    26 
    27             self.withdraw(count)
    28 
    29 
    30 def transfer(_from, to, amount):
    31 
    32     #锁不可以加在这里 因为其他的其它线程执行的其它方法在不加锁的情况下数据同样是不安全的
    33      _from.withdraw(amount)
    34 
    35      to.deposit(amount)
    36 
    37 
    38 
    39 alex = Account('kelvin',1000)
    40 yuan = Account('bob',1000)
    41 
    42 t1=threading.Thread(target = transfer, args = (kelvin,bob, 100))
    43 t1.start()
    44 
    45 t2=threading.Thread(target = transfer, args = (bob,kelvin, 200))
    46 t2.start()
    47 
    48 t1.join()
    49 t2.join()
    50 
    51 print('>>>',kelvin.balance)
    52 print('>>>',bob.balance)

    五,同步条件(Event):

    事件是一个简单的同步对象;该事件表示一个内部标志,
    线程可以等待设置标志,或者自己设置或清除标志。
    event = threading.Event()


    #客户端线程可以等待设置标志
    event.wait()


    #服务器线程可以设置或重置它

    event.set()
    event.clear()


    如果设置了标志,则wait方法不执行任何操作。
    如果该标志被清除,等待将被阻止,直到它再次设置。
    任何数量的线程都可以等待相同的事件。

     1 import threading,time
     2 class Boss(threading.Thread):
     3     def run(self):
     4         print("BOSS:今晚大家都要加班到22:00。")
     5         print(event.isSet())
     6         event.set()
     7         time.sleep(5)
     8         print("BOSS:<22:00>可以下班了。")
     9         print(event.isSet())
    10         event.set()
    11 class Worker(threading.Thread):
    12     def run(self):
    13         event.wait()
    14         print("Worker:哎……命苦啊!")
    15         time.sleep(1)
    16         event.clear()
    17         event.wait()
    18         print("Worker:OhYeah!")
    19 if __name__=="__main__":
    20     event=threading.Event()
    21     threads=[]
    22     for i in range(5):
    23         threads.append(Worker())
    24     threads.append(Boss())
    25     for t in threads:
    26         t.start()
    27     for t in threads:
    28         t.join()

    六,信号量(Semaphore)

          信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。

          计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

          BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

     1 import threading,time
     2 class myThread(threading.Thread):
     3     def run(self):
     4         if semaphore.acquire():
     5             print(self.name)
     6             time.sleep(5)
     7             semaphore.release()
     8 if __name__=="__main__":
     9     semaphore=threading.Semaphore(5)
    10     thrs=[]
    11     for i in range(100):
    12         thrs.append(myThread())
    13     for t in thrs:
    14         t.start()

    七,多线程利器---队列(queue)

    列表是不安全的数据结构:

     1 import threading,time
     2 
     3 li=[1,2,3,4,5]
     4 
     5 def pri():
     6     while li:
     7         a=li[-1]
     8         print(a)
     9         time.sleep(1)
    10         try:
    11             li.remove(a)
    12         except Exception as e:
    13             print('----',a,e)
    14 
    15 t1=threading.Thread(target=pri,args=())
    16 t1.start()
    17 t2=threading.Thread(target=pri,args=())
    18 t2.start()

    思考:如何通过对列来完成上述功能?

    queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.

    queue列队类的方法:

     1 创建一个“队列”对象
     2 import queue
     3 q = queue.Queue(maxsize = 10)
     4 queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
     5 
     6 将一个值放入队列中
     7 q.put(10)
     8 调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
     9 1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。
    10 
    11 将一个值从队列中取出
    12 q.get()
    13 调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,
    14 get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
    15 
    16 Python Queue模块有三种队列及构造函数:
    17 1、Python Queue模块的FIFO队列先进先出。   class queue.Queue(maxsize)
    18 2、LIFO类似于堆,即先进后出。               class queue.LifoQueue(maxsize)
    19 3、还有一种是优先级队列级别越低越先出来。        class queue.PriorityQueue(maxsize)
    20 
    21 此包中的常用方法(q = queue.Queue()):
    22 q.qsize() 返回队列的大小
    23 q.empty() 如果队列为空,返回True,反之False
    24 q.full() 如果队列满了,返回True,反之False
    25 q.full 与 maxsize 大小对应
    26 q.get([block[, timeout]]) 获取队列,timeout等待时间
    27 q.get_nowait() 相当q.get(False)
    28 非阻塞 q.put(item) 写入队列,timeout等待时间
    29 q.put_nowait(item) 相当q.put(item, False)
    30 q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
    31 q.join() 实际上意味着等到队列为空,再执行别的操作

    other mode:

     1 import queue
     2 
     3 #先进后出
     4 
     5 q=queue.LifoQueue()
     6 
     7 q.put(34)
     8 q.put(56)
     9 q.put(12)
    10 
    11 #优先级
    12 # q=queue.PriorityQueue()
    13 # q.put([5,100])
    14 # q.put([7,200])
    15 # q.put([3,"hello"])
    16 # q.put([4,{"name":"alex"}])
    17 
    18 while 1:
    19 
    20   data=q.get()
    21   print(data)

    生产者消费者模型:

    为什么要使用生产者和消费者模式?

    在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

    什么是生产者消费者模式?

    生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

    这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个结耦的过程。

     1 import time,random
     2 import queue,threading
     3 
     4 q = queue.Queue()
     5 
     6 def Producer(name):
     7   count = 0
     8   while count <10:
     9     print("making........")
    10     time.sleep(random.randrange(3))
    11     q.put(count)
    12     print('Producer %s has produced %s baozi..' %(name, count))
    13     count +=1
    14     #q.task_done()
    15     #q.join()
    16     print("ok......")
    17 def Consumer(name):
    18   count = 0
    19   while count <10:
    20     time.sleep(random.randrange(4))
    21     if not q.empty():
    22         data = q.get()
    23         #q.task_done()
    24         #q.join()
    25         print(data)
    26         print('33[32;1mConsumer %s has eat %s baozi...33[0m' %(name, data))
    27     else:
    28         print("-----no baozi anymore----")
    29     count +=1
    30 
    31 p1 = threading.Thread(target=Producer, args=('A',))
    32 c1 = threading.Thread(target=Consumer, args=('B',))
    33 # c2 = threading.Thread(target=Consumer, args=('C',))
    34 # c3 = threading.Thread(target=Consumer, args=('D',))
    35 p1.start()
    36 c1.start()
    37 # c2.start()
    38 # c3.start()

    <多进程模块 multiprocessing>

    Multiprocessing is a package that supports spawning processes using an API similar to the threading module. The multiprocessing package offers both local and remote concurrency,effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.

    由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。

    multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。

    一 ,进程的调用

    调用方式1

     1 from multiprocessing import Process
     2 import time
     3 def f(name):
     4     time.sleep(1)
     5     print('hello', name,time.ctime())
     6 
     7 if __name__ == '__main__':
     8     p_list=[]
     9     for i in range(3):
    10         p = Process(target=f, args=('alvin',))
    11         p_list.append(p)
    12         p.start()
    13     for i in p_list:
    14         p.join()
    15     print('end')

     调用方式2

     1 from multiprocessing import Process
     2 import time
     3 
     4 class MyProcess(Process):
     5     def __init__(self):
     6         super(MyProcess, self).__init__()
     7         #self.name = name
     8 
     9     def run(self):
    10         time.sleep(1)
    11         print ('hello', self.name,time.ctime())
    12 
    13 
    14 if __name__ == '__main__':
    15     p_list=[]
    16     for i in range(3):
    17         p = MyProcess()
    18         p.start()
    19         p_list.append(p)
    20 
    21     for p in p_list:
    22         p.join()
    23 
    24     print('end')

     要显示涉及的各个进程ID,下面是一个扩展的示例:

     1 from multiprocessing import Process
     2 import os
     3 import time
     4 def info(title):
     5   
     6     print("title:",title)
     7     print('parent process:', os.getppid())
     8     print('process id:', os.getpid())
     9 
    10 def f(name):
    11     info('function f')
    12     print('hello', name)
    13 
    14 if __name__ == '__main__':
    15     info('main process line')
    16     time.sleep(1)
    17     print("------------------")
    18     p = Process(target=info, args=('yuan',))
    19     p.start()
    20     p.join()

    二, Process类 

    构造方法:

    Process([group [, target [, name [, args [, kwargs]]]]])

      group: 线程组,目前还没有实现,库引用中提示必须是None; 
      target: 要执行的方法; 
      name: 进程名; 
      args/kwargs: 要传入方法的参数。

    实例方法:

      is_alive():返回进程是否在运行。

      join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

      start():进程准备就绪,等待CPU调度

      run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

      terminate():不管任务是否完成,立即停止工作进程

    属性:

      daemon:和线程的setDeamon功能一样

      name:进程名字。

      pid:进程号。

     1 # Author : Kelvin
     2 # Date : 2019/2/13 13:31
     3 from multiprocessing import Process
     4 import time
     5 
     6 
     7 def foo(name):
     8     time.sleep(1)
     9     print("%s,当前时间:%s" % (name, time.strftime("%Y-%m-%d %X", time.localtime())))
    10 
    11 
    12 if __name__ == "__main__":
    13     p_list = []
    14     for i in range(4):
    15         p = Process(target=foo, args=("kelvin",))
    16         p_list.append(p)
    17         p.start()
    18         print("%s进程是否活跃?%s  进程号:%s"%(p.name,p.is_alive(),p.pid))
    19         # p.join()
    20     for i in p_list:
    21         i.join()
    22     print("This program is over!")

    三 ,进程间通讯 

    1 进程队列Queue

     1 from multiprocessing import Process, Queue
     2 import queue
     3 
     4 def f(q,n):
     5     #q.put([123, 456, 'hello'])
     6     q.put(n*n+1)
     7     print("son process",id(q))
     8 
     9 if __name__ == '__main__':
    10     q = Queue()  #try: q=queue.Queue()
    11     print("main process",id(q))
    12 
    13     for i in range(3):
    14         p = Process(target=f, args=(q,i))
    15         p.start()
    16 
    17     print(q.get())
    18     print(q.get())
    19     print(q.get())

    2 管道

    函数的作用是:返回一对由管道连接的连接对象,默认情况下,管道是双向的。例如:

     1 from multiprocessing import Process, Pipe
     2 
     3 def f(conn):
     4     conn.send([12, {"name":"yuan"}, 'hello'])
     5     response=conn.recv()
     6     print("response",response)
     7     conn.close()
     8     print("q_ID2:",id(child_conn))
     9 
    10 if __name__ == '__main__':
    11 
    12     parent_conn, child_conn = Pipe()
    13     print("q_ID1:",id(child_conn))
    14     p = Process(target=f, args=(child_conn,))
    15     p.start()
    16     print(parent_conn.recv())   # prints "[42, None, 'hello']"
    17     parent_conn.send("儿子你好!")
    18     p.join()

    pipe()返回的两个连接对象表示管道的两端。每个连接对象都有send()和recv()方法(以及其他方法)。请注意,如果两个进程(或线程)同时尝试从管道的同一端读取或写入数据,则管道中的数据可能会损坏。当然,同时使用不同管端的过程不会有损坏的风险。

    3 Managers

    Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。

    manager()返回的manager对象控制包含python对象的服务器进程,并允许其他进程使用代理来操作这些对象。
    manager()返回的管理器将支持类型列表、dict、名称空间、锁、rlock、信号量、有界信号量、条件、事件、屏障、队列、值和数组。例如:

     1 from multiprocessing import Process, Manager
     2 
     3 def f(d, l,n):
     4     d[n] = '1'
     5     d['2'] = 2
     6     d[0.25] = None
     7     l.append(n)
     8     #print(l)
     9 
    10     print("son process:",id(d),id(l))
    11 
    12 if __name__ == '__main__':
    13 
    14     with Manager() as manager:
    15 
    16         d = manager.dict()
    17 
    18         l = manager.list(range(5))
    19 
    20         print("main process:",id(d),id(l))
    21 
    22         p_list = []
    23 
    24         for i in range(10):
    25             p = Process(target=f, args=(d,l,i))
    26             p.start()
    27             p_list.append(p)
    28 
    29         for res in p_list:
    30             res.join()
    31 
    32         print(d)
    33         print(l)

    四 ,进程同步

    如果不使用来自不同进程的锁输出,则很容易混淆所有内容。

     1 from multiprocessing import Process, Lock
     2 
     3 def f(l, i):
     4   
     5     with l.acquire():
     6         print('hello world %s'%i)
     7 
     8 if __name__ == '__main__':
     9     lock = Lock()
    10 
    11     for num in range(10):
    12         Process(target=f, args=(lock, num)).start()

    五 ,进程池

    进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

    进程池中有两个方法:

    • apply
    • apply_async
     1 from  multiprocessing import Process,Pool
     2 import time,os
     3 
     4 def Foo(i):
     5     time.sleep(1)
     6     print(i)
     7     return i+100
     8 
     9 def Bar(arg):
    10 
    11     print(os.getpid())
    12     print(os.getppid())
    13     print('logger:',arg)
    14 
    15 pool = Pool(5)
    16 
    17 Bar(1)
    18 print("----------------")
    19 
    20 for i in range(10):
    21     #pool.apply(func=Foo, args=(i,))
    22     #pool.apply_async(func=Foo, args=(i,))
    23     pool.apply_async(func=Foo, args=(i,),callback=Bar)
    24 
    25 pool.close()
    26 pool.join()
    27 print('end')

     

    <Python中的上下文管理器(contextlib模块)>

    上下文管理器的任务是:代码块执行前准备,代码块执行后收拾

    1 如何使用上下文管理器:

    如何打开一个文件,并写入"hello world":

    1 filename="my.txt"
    2 mode="w"
    3 f=open(filename,mode)
    4 f.write("hello world")
    5 f.close()

    当发生异常时(如磁盘写满),就没有机会执行第5行。当然,我们可以采用try-finally语句块进行包装:

    1 writer=open(filename,mode)
    2 try:
    3     writer.write("hello world")
    4 finally:
    5     writer.close()

    当我们进行复杂的操作时,try-finally语句就会变得丑陋,采用with语句重写:

    1 with open(filename,mode) as writer:
    2     writer.write("hello world")

    as指代了从open()函数返回的内容,并把它赋给了新值。with完成了try-finally的任务。

     

    2 自定义上下文管理器  

    with语句的作用类似于try-finally,提供一种上下文机制。要应用with语句的类,其内部必须提供两个内置函数__enter__和__exit__。前者在主体代码执行前执行,后者在主体代码执行后执行。as后面的变量,是在__enter__函数中返回的。

     1 class echo():
     2     def output(self):
     3         print "hello world"
     4     def __enter__(self):
     5         print "enter"
     6         return self  #可以返回任何希望返回的东西
     7     def __exit__(self,exception_type,value,trackback):
     8         print "exit"
     9         if exception_type==ValueError:
    10             return True
    11         else:
    12             return Flase
    13   
    14 >>>with echo as e:
    15     e.output()
    16      
    17 输出:
    18 enter
    19 hello world
    20 exit

    完备的__exit__函数如下:

    1 def __exit__(self,exc_type,exc_value,exc_tb)

    其中,exc_type:异常类型;exc_value:异常值;exc_tb:异常追踪信息

    当__exit__返回True时,异常不传播

    3 ,contextlib模块  

    contextlib模块的作用是提供更易用的上下文管理器,它是通过Generator实现的。contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制,常用框架如下:

     1 from contextlib import contextmanager
     2 @contextmanager
     3 def make_context():
     4     print 'enter'
     5     try:
     6         yield "ok"
     7     except RuntimeError,err:
     8         print 'error',err
     9     finally:
    10         print 'exit'
    11          
    12 >>>with make_context() as value:
    13     print value
    14      
    15 输出为:
    16     enter
    17     ok
    18     exit

    其中,yield写入try-finally中是为了保证异常安全(能处理异常)as后的变量的值是由yield返回。yield前面的语句可看作代码块执行前操作,yield之后的操作可以看作在__exit__函数中的操作。

    以线程锁为例:

     1 @contextlib.contextmanager
     2 def loudLock():
     3     print 'Locking'
     4     lock.acquire()
     5     yield
     6     print 'Releasing'
     7     lock.release()
     8  
     9 with loudLock():
    10     print 'Lock is locked: %s' % lock.locked()
    11     print 'Doing something that needs locking'
    12  
    13 #Output:
    14 #Locking
    15 #Lock is locked: True
    16 #Doing something that needs locking
    17 #Releasing

    4 contextlib.nested:减少嵌套

    对于:

    1 with open(filename,mode) as reader:
    2     with open(filename1,mode1) as writer:
    3         writer.write(reader.read())

    可以通过contextlib.nested进行简化:

    1 with contextlib.nested(open(filename,mode),open(filename1,mode1)) as (reader,writer):
    2     writer.write(reader.read())

    在python 2.7及以后,被一种新的语法取代:

    1 with open(filename,mode) as reader,open(filename1,mode1) as writer:
    2     writer.write(reader.read())

    5 contextlib.closing() 

    file类直接支持上下文管理器API,但有些表示打开句柄的对象并不支持,如urllib.urlopen()返回的对象。还有些遗留类,使用close()方法而不支持上下文管理器API。为了确保关闭句柄,需要使用closing()为它创建一个上下文管理器(调用类的close方法)。

    <协程>

    协程,又称微线程,纤程。英文名Coroutine。

    优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

    优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

    因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

    yield的简单实现

     1 import time
     2 import queue
     3 
     4 def consumer(name):
     5     print("--->ready to eat baozi...")
     6     while True:
     7         new_baozi = yield
     8         print("[%s] is eating baozi %s" % (name,new_baozi))
     9         #time.sleep(1)
    10 
    11 def producer():
    12 
    13     r = con.__next__()
    14     r = con2.__next__()
    15     n = 0
    16     while 1:
    17         time.sleep(1)
    18         print("33[32;1m[producer]33[0m is making baozi %s and %s" %(n,n+1) )
    19         con.send(n)
    20         con2.send(n+1)
    21 
    22         n +=2
    23 
    24 
    25 if __name__ == '__main__':
    26     con = consumer("c1")
    27     con2 = consumer("c2")
    28     p = producer()

    Greenlet

    greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

     1 from greenlet import greenlet
     2  
     3  
     4 def test1():
     5     print(12)
     6     gr2.switch()
     7     print(34)
     8     gr2.switch()
     9  
    10  
    11 def test2():
    12     print(56)
    13     gr1.switch()
    14     print(78)
    15  
    16  
    17 gr1 = greenlet(test1)
    18 gr2 = greenlet(test2)
    19 gr1.switch()

    Gevent

     1 import gevent
     2 
     3 import requests,time
     4 
     5 
     6 start=time.time()
     7 
     8 def f(url):
     9     print('GET: %s' % url)
    10     resp =requests.get(url)
    11     data = resp.text
    12     print('%d bytes received from %s.' % (len(data), url))
    13 
    14 gevent.joinall([
    15 
    16         gevent.spawn(f, 'https://www.python.org/'),
    17         gevent.spawn(f, 'https://www.yahoo.com/'),
    18         gevent.spawn(f, 'https://www.baidu.com/'),
    19         gevent.spawn(f, 'https://www.sina.com.cn/'),
    20 
    21 ])
    22 
    23 # f('https://www.python.org/')
    24 #
    25 # f('https://www.yahoo.com/')
    26 #
    27 # f('https://baidu.com/')
    28 #
    29 # f('https://www.sina.com.cn/')
    30 
    31 print("cost time:",time.time()-start)
  • 相关阅读:
    一个程序员的职业规划
    基于Andoird 4.2.2的Account Manager源代码分析学习:创建选定类型的系统帐号
    [置顶] C++学习书单
    js快速分享代码
    The declared package does not match the expected package
    IBM Rational Appscan Part 1
    IBM Rational Appscan: Part 2 ---reference
    阅读redis源代码的一些体会
    18 Command Line Tools to Monitor Linux Performance
    代码规范
  • 原文地址:https://www.cnblogs.com/sun-10387834/p/10356292.html
Copyright © 2020-2023  润新知