并发编程(二)

同步条件(Event)

An event is a simple synchronization object;the event represents an internal flag,

and threads can wait for the flag to be set, or set or clear the flag themselves.


event = threading.Event()

# a client thread can wait for the flag to be set
event.wait()   #植入同步

# a server thread can set or reset it
event.set()     #触发执行
event.clear() #拆除同步

信号量(Semaphore)

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

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

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

 
import threading,time
class myThread(threading.Thread):
    def run(self):
        if semaphore.acquire():
            print(self.name)
            time.sleep(5)
            semaphore.release()
if __name__=="__main__":
    semaphore=threading.Semaphore(5)
    thrs=[]
    for i in range(100):
        thrs.append(myThread())
    for t in thrs:
        t.start() 

队列(queue)

多线程下使用列表数据结构并不安全

queue列队类的方法

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

生产者消费者模型:

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

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

什么是生产者消费者模式

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

 

 多进程模块 multiprocessing

由于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

 
from multiprocessing import Process
import time
def f(name):
    time.sleep(1)
    print('hello', name,time.ctime())

if __name__ == '__main__':
    p_list=[]
    for i in range(3):
        p = Process(target=f, args=('alvin',))
        p_list.append(p)
        p.start()
    for i in p_list:
        p.join()
    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')

实例

from multiprocessing import Process
import os
import time
def info(title):
  
    print("title:",title)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())

def f(name):
    info('function f')
    print('hello', name)

if __name__ == '__main__':
    info('main process line')
    time.sleep(1)
    print("------------------")
    p = Process(target=info, args=('yuan',))
    p.start()
    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:进程号。

进程间通讯 

 进程队列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())

 

管道

 1 from multiprocessing import Process, Pipe
 2 
 3 def f(conn):
 4     conn.send([12, '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()

Managers实现数据共享

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

 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)

进程池

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

进程池中有两个方法:

  • apply
  • apply_async

#!/usr/bin/python
# -*- coding: utf-8 -*-

from multiprocessing import Process,Pool
import time,os

def Foo(i):
time.sleep(1)
print(i)
return i+100

def Bar(arg):

print(os.getpid())
print(os.getppid())
print('hello,logger:',arg)

if __name__ == '__main__':
pool = Pool(5) #默认为计算机CPU数
for i in range(100):
# pool.apply(func=Foo, args=(i,)) #同步
#pool.apply_async(func=Foo, args=(i,))
pool.apply_async(func=Foo, args=(i,),callback=Bar) #异步
# 回调函数:就是某个动作或者函数执行成功后再去执行的函数
pool.close() #进程池中close 在 join之前
pool.join()
print('end')

 

协程

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

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

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

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

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)

 

 

 

 

 

 

 

 

 

 

posted @ 2018-10-11 20:55  Roygood  阅读(142)  评论(0编辑  收藏  举报