1. 进程、与线程区别
  2. cpu运行原理
  3. python GIL全局解释器锁
  4. 线程
    1. 语法
    2. join
    3. 线程锁之Lock\Rlock\信号量
    4. 将线程变为守护进程
    5. Event事件 
    6. queue队列
    7. 生产者消费者模型
    8. Queue队列
    9. 开发一个线程池
  5. 进程
    1. 语法
    2. 进程间通讯
    3. 进程池    

进程与线程

什么是线程(thread)?

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

A thread is an execution context, which is all the information a CPU needs to execute a stream of instructions.

Suppose you're reading a book, and you want to take a break right now, but you want to be able to come back and resume reading from the exact point where you stopped. One way to achieve that is by jotting down the page number, line number, and word number. So your execution context for reading a book is these 3 numbers.

If you have a roommate, and she's using the same technique, she can take the book while you're not using it, and resume reading from where she stopped. Then you can take it back, and resume it from where you were.

Threads work in the same way. A CPU is giving you the illusion that it's doing multiple computations at the same time. It does that by spending a bit of time on each computation. It can do that because it has an execution context for each computation. Just like you can share a book with your friend, many tasks can share a CPU.

On a more technical level, an execution context (therefore a thread) consists of the values of the CPU's registers.

Last: threads are different from processes. A thread is a context of execution, while a process is a bunch of resources associated with a computation. A process can have one or many threads.

Clarification: the resources associated with a process include memory pages (all the threads in a process have the same view of the memory), file descriptors (e.g., open sockets), and security credentials (e.g., the ID of the user who started the process).

什么是进程(process)?

An executing instance of a program is called a process.

Each process provides the resources needed to execute a program. A process has a virtual address space, executable code, open handles to system objects, a security context, a unique process identifier, environment variables, a priority class, minimum and maximum working set sizes, and at least one thread of execution. Each process is started with a single thread, often called the primary thread, but can create additional threads from any of its threads.

进程与线程的区别?

    • Threads share the address space of the process that created it; processes have their own address space.
    • Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.
    • Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.
    • New threads are easily created; new processes require duplication of the parent process.
    • Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.
    • Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes 。  

Python threading模块

线程有2种调用方式,如下:

直接调用

复制代码
 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 class MyThread(threading.Thread):
 5     def __init__(self,num):
 6         threading.Thread.__init__(self)
 7         self.num = num
 8  
 9     def run(self):#定义每个线程要运行的函数
10  
11         print("running on number:%s" %self.num)
12  
13         time.sleep(3)
14  
15 if __name__ == '__main__':
16  
17     t1 = MyThread(1)
18     t2 = MyThread(2)
19     t1.start()
20     t2.start()
复制代码

Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。

复制代码
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import threading
 4 import time
 5   
 6 def show(arg):
 7     time.sleep(1)
 8     print 'thread'+str(arg)
 9   
10 for i in range(10):
11     t = threading.Thread(target=show, args=(i,))
12     t.start()
13   
14 print 'main thread stop'
复制代码

上述代码创建了10个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。

更多方法:

    • start 线程准备就绪,等待CPU调度
    • setName 为线程设置名称
    • getName 获取线程名称
    • setDaemon 设置为后台线程或前台线程(默认)
      如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
      如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
    • join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
    • run 线程被cpu调度后自动执行线程对象的run方法

Join & Daemon

Some threads do background tasks, like sending keepalive packets, or performing periodic garbage collection, or whatever. These are only useful when the main program is running, and it's okay to kill them off once the other, non-daemon, threads have exited.

Without daemon threads, you'd have to keep track of them, and tell them to exit, before your program can completely quit. By setting them as daemon threads, you can let them run and forget about them, and when your program quits, any daemon threads are killed automatically.

复制代码
 1 import time
 2 import threading
 3  
 4 def run(n):
 5  
 6     print('[%s]------running----\n' % n)
 7     time.sleep(2)
 8     print('--done--')
 9  
10 def main():
11     for i in range(5):
12         t = threading.Thread(target=run,args=[i,])
13         #time.sleep(1)
14         t.start()
15         t.join(1)
16         print('starting thread', t.getName())
17  
18  
19 m = threading.Thread(target=main,args=[])
20 m.setDaemon(True) #将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务
21 m.start()
22 #m.join(timeout=2)
23 print("---main thread done----")
复制代码

Note:Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.

  

线程锁(互斥锁Mutex)

一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况?

复制代码
 1 import time
 2 import threading
 3  
 4 def addNum():
 5     global num #在每个线程中都获取这个全局变量
 6     print('--get num:',num )
 7     time.sleep(1)
 8     num  -=1 #对此公共变量进行-1操作
 9  
10 num = 100  #设定一个共享变量
11 thread_list = []
12 for i in range(100):
13     t = threading.Thread(target=addNum)
14     t.start()
15     thread_list.append(t)
16  
17 for t in thread_list: #等待所有线程执行完毕
18     t.join()
19  
20  
21 print('final num:', num )
复制代码

正常来讲,这个num结果应该是0, 但在python 2.7上多运行几次,会发现,最后打印出来的num结果不总是0,为什么每次运行的结果不一样呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操作, 由于2个线程是并发同时运行的,所以2个线程很有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。

*注:不要在3.x上运行,不知为什么,3.x上的结果总是正确的,可能是自动加了锁

加锁版本

复制代码
 1 import time
 2 import threading
 3  
 4 def addNum():
 5     global num #在每个线程中都获取这个全局变量
 6     print('--get num:',num )
 7     time.sleep(1)
 8     lock.acquire() #修改数据前加锁
 9     num  -=1 #对此公共变量进行-1操作
10     lock.release() #修改后释放
11  
12 num = 100  #设定一个共享变量
13 thread_list = []
14 lock = threading.Lock() #生成全局锁
15 for i in range(100):
16     t = threading.Thread(target=addNum)
17     t.start()
18     thread_list.append(t)
19  
20 for t in thread_list: #等待所有线程执行完毕
21     t.join()
22  
23 print('final num:', num )
复制代码

  

RLock(递归锁)

说白了就是在一个大锁中还要再包含子锁

复制代码
 1 import threading,time
 2  
 3 def run1():
 4     print("grab the first part data")
 5     lock.acquire()
 6     global num
 7     num +=1
 8     lock.release()
 9     return num
10 def run2():
11     print("grab the second part data")
12     lock.acquire()
13     global  num2
14     num2+=1
15     lock.release()
16     return num2
17 def run3():
18     lock.acquire()
19     res = run1()
20     print('--------between run1 and run2-----')
21     res2 = run2()
22     lock.release()
23     print(res,res2)
24  
25  
26 if __name__ == '__main__':
27  
28     num,num2 = 0,0
29     lock = threading.RLock()
30     for i in range(10):
31         t = threading.Thread(target=run3)
32         t.start()
33  
34 while threading.active_count() != 1:
35     print(threading.active_count())
36 else:
37     print('----all threads done---')
38     print(num,num2)
复制代码

  

Semaphore(信号量)

互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

复制代码
 1 import threading,time
 2  
 3 def run(n):
 4     semaphore.acquire()
 5     time.sleep(1)
 6     print("run the thread: %s\n" %n)
 7     semaphore.release()
 8  
 9 if __name__ == '__main__':
10  
11     num= 0
12     semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
13     for i in range(20):
14         t = threading.Thread(target=run,args=(i,))
15         t.start()
16  
17 while threading.active_count() != 1:
18     pass #print threading.active_count()
19 else:
20     print('----all threads done---')
21     print(num)
复制代码

Events

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()
If the flag is set, the wait method doesn’t do anything.
If the flag is cleared, wait will block until it becomes set again.
Any number of threads may wait for the same event.

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

clear:将“Flag”设置为False
set:将“Flag”设置为True

通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。

复制代码
 1 import threading,time
 2 import random
 3 def light():
 4     if not event.isSet():
 5         event.set() #wait就不阻塞 #绿灯状态
 6     count = 0
 7     while True:
 8         if count < 10:
 9             print('\033[42;1m--green light on---\033[0m')
10         elif count <13:
11             print('\033[43;1m--yellow light on---\033[0m')
12         elif count <20:
13             if event.isSet():
14                 event.clear()
15             print('\033[41;1m--red light on---\033[0m')
16         else:
17             count = 0
18             event.set() #打开绿灯
19         time.sleep(1)
20         count +=1
21 def car(n):
22     while 1:
23         time.sleep(random.randrange(10))
24         if  event.isSet(): #绿灯
25             print("car [%s] is running.." % n)
26         else:
27             print("car [%s] is waiting for the red light.." %n)
28 if __name__ == '__main__':
29     event = threading.Event()
30     Light = threading.Thread(target=light)
31     Light.start()
32     for i in range(3):
33         t = threading.Thread(target=car,args=(i,))
34         t.start()
复制代码

  

queue队列

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

class queue.Queue(maxsize=0) #先入先出
class queue.LifoQueue(maxsize=0) #last in fisrt out class queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列

Constructor for a priority queue. maxsize is an integer that sets the upperbound limit on the number of items that can be placed in the queue. Insertion will block once this size has been reached, until queue items are consumed. Ifmaxsize is less than or equal to zero, the queue size is infinite.

The lowest valued entries are retrieved first (the lowest valued entry is the one returned bysorted(list(entries))[0]). A typical pattern for entries is a tuple in the form:(priority_number, data).

exceptionqueue.Empty

Exception raised when non-blocking get() (or get_nowait()) is called on aQueue object which is empty.

exception queue.Full

Exception raised when non-blocking put() (or put_nowait()) is called on aQueue object which is full.

Queue. qsize ()
Queue. empty () #return True if empty
Queue. full () # return True if full
Queue. put (item,block=True, timeout=None)

Put item into the queue. If optional args block is true and timeout is None (the default), block if necessary until a free slot is available. Iftimeout is a positive number, it blocks at most timeout seconds and raises theFull exception if no free slot was available within that time. Otherwise (block is false), put an item on the queue if a free slot is immediately available, else raise theFull exception (timeout is ignored in that case).

Queue. put_nowait (item)

Equivalent to put(item, False).

Queue. get (block=True,timeout=None)

Remove and return an item from the queue. If optional args block is true andtimeout is None (the default), block if necessary until an item is available. Iftimeout is a positive number, it blocks at most timeout seconds and raises theEmpty exception if no item was available within that time. Otherwise (block is false), return an item if one is immediately available, else raise theEmpty exception (timeout is ignored in that case).

Queue. get_nowait ()

Equivalent to get(False).

Two methods are offered to support tracking whether enqueued tasks have been fully processed by daemon consumer threads.

Queue. task_done ()

Indicate that a formerly enqueued task is complete. Used by queue consumer threads. For eachget() used to fetch a task, a subsequent call totask_done() tells the queue that the processing on the task is complete.

If a join() is currently blocking, it will resume when all items have been processed (meaning that atask_done() call was received for every item that had beenput() into the queue).

Raises a ValueError if called more times than there were items placed in the queue.

Queue. join () block直到queue被消费完毕

生产者消费者模型

复制代码
 1 import time,random
 2 import queue,threading
 3 q = queue.Queue()
 4 def Producer(name):
 5   count = 0
 6   while count <20:
 7     time.sleep(random.randrange(3))
 8     q.put(count)
 9     print('Producer %s has produced %s baozi..' %(name, count))
10     count +=1
11 def Consumer(name):
12   count = 0
13   while count <20:
14     time.sleep(random.randrange(4))
15     if not q.empty():
16         data = q.get()
17         print(data)
18         print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
19     else:
20         print("-----no baozi anymore----")
21     count +=1
22 p1 = threading.Thread(target=Producer, args=('A',))
23 c1 = threading.Thread(target=Consumer, args=('B',))
24 p1.start()
25 c1.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 theGlobal Interpreter Lock by using subprocesses instead of threads. Due to this, themultiprocessing module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.

复制代码
 1 from multiprocessing import Process
 2 import time
 3 def f(name):
 4     time.sleep(2)
 5     print('hello', name)
 6  
 7 if __name__ == '__main__':
 8     p = Process(target=f, args=('bob',))
 9     p.start()
10     p.join()
复制代码

注意:由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销。

To show the individual process IDs involved, here is an expanded example:

复制代码
 1 from multiprocessing import Process
 2 import os
 3  
 4 def info(title):
 5     print(title)
 6     print('module name:', __name__)
 7     print('parent process:', os.getppid())
 8     print('process id:', os.getpid())
 9     print("\n\n")
10  
11 def f(name):
12     info('\033[31;1mfunction f\033[0m')
13     print('hello', name)
14  
15 if __name__ == '__main__':
16     info('\033[32;1mmain process line\033[0m')
17     p = Process(target=f, args=('bob',))
18     p.start()
19     p.join()
复制代码

进程间通讯  

不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:

Queues

使用方法跟threading里的queue差不多

复制代码
 1 from multiprocessing import Process, Queue
 2  
 3 def f(q):
 4     q.put([42, None, 'hello'])
 5  
 6 if __name__ == '__main__':
 7     q = Queue()
 8     p = Process(target=f, args=(q,))
 9     p.start()
10     print(q.get())    # prints "[42, None, 'hello']"
11     p.join()
复制代码

Pipes

The Pipe() function returns a pair of connection objects connected by a pipe which by default is duplex (two-way). For example:

复制代码
 1 from multiprocessing import Process, Pipe
 2  
 3 def f(conn):
 4     conn.send([42, None, 'hello'])
 5     conn.close()
 6  
 7 if __name__ == '__main__':
 8     parent_conn, child_conn = Pipe()
 9     p = Process(target=f, args=(child_conn,))
10     p.start()
11     print(parent_conn.recv())   # prints "[42, None, 'hello']"
12     p.join()
复制代码

The two connection objects returned by Pipe() represent the two ends of the pipe. Each connection object hassend() and recv() methods (among others). Note that data in a pipe may become corrupted if two processes (or threads) try to read from or write to thesame end of the pipe at the same time. Of course there is no risk of corruption from processes using different ends of the pipe at the same time.

Managers

A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.

A manager returned by Manager() will support typeslist, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array. For example,

复制代码
#没有共享数据
from multiprocessing import Process
import time
li = []

def foo(i):
    li.append(i)
    print(''say hi",li)

if __name__=='__main__':
    for i in range(10):
        p=Process(target=foo,args=(i,))
        p.start()

print('ending',li)

#方法一:Array
from multiprocessing import Process,Array
temp = Array('i', [11,22,33,44])
 
def Foo(i):
    temp[i] = 100+i
    for item in temp:
        print i,'----->',item
if __name__=='__main__': 
    for i in range(2):
        p = Process(target=Foo,args=(i,))
        p.start()

#方法二:manage.dict()共享数据
from multiprocessing import Process, Manager

def f(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.append(1)
    print(l)

if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()
        l = manager.list(range(5))
        p_list = []
        for i in range(10):
            p = Process(target=f, args=(d, l))
            p.start()
            p_list.append(p)
        for res in p_list:
            res.join()
        print(d)
复制代码

当创建进程时(非使用时),共享数据会被拿到子进程中,当进程中执行完毕后,再赋值给原值。

进程锁实例

复制代码
 1 from multiprocessing import Process, Array, RLock
 2 
 3 def Foo(lock,temp,i):
 4     """
 5     将第0个数加100
 6     """
 7     lock.acquire()
 8     temp[0] = 100+i
 9     for item in temp:
10         print(i,'----->',item)
11     lock.release()
12 
13 lock = RLock()
14 temp = Array('i', [11, 22, 33, 44])
15 if __name__=='__main__':
16 
17     for i in range(20):
18         p = Process(target=Foo,args=(lock,temp,i,))
19         p.start()
复制代码

进程同步

Without using the lock output from the different processes is liable to get all mixed up.

复制代码
 1 from multiprocessing import Process, Lock
 2  
 3 def f(l, i):
 4     l.acquire()
 5     try:
 6         print('hello world', i)
 7     finally:
 8         l.release()
 9  
10 if __name__ == '__main__':
11     lock = Lock()
12  
13     for num in range(10):
14         Process(target=f, args=(lock, num)).start()
复制代码

  

进程池  

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

进程池中有两个方法:

  • apply
  • apply_async
复制代码
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 from  multiprocessing import Process,Pool
 4 import time
 5 
 6 def Foo(i):
 7     time.sleep(5)
 8     print('第%s次'%i)
 9     return i+100
10 def Bar(arg):
11     print('what-->',arg)
12 #print pool.apply(Foo,(1,))
13 #print pool.apply_async(func =Foo, args=(1,)).get()
14 if __name__=='__main__':
15     pool = Pool(5)  #创建5个有进程的进程池
16     for i in range(10):
17         pool.apply_async(func=Foo, args=(i,),callback=Bar) #callback是回调
18     print('end')
19     pool.close() #先写close,再写join
20     pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
复制代码

协程

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

协程的好处:

无需线程上下文切换的开销
无需原子操作锁定及同步的开销
方便切换控制流,简化编程模型
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:

无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
使用yield实现协程操作例子

复制代码
 1 import time
 2 import queue
 3 def consumer(name):
 4     print("--->starting eating baozi...")
 5     while True:
 6         new_baozi = yield
 7         print("[%s] is eating baozi %s" % (name,new_baozi))
 8         #time.sleep(1)
 9 
10 def producer():
11     r = con.__next__()
12     r = con2.__next__()
13     n = 0
14     while n < 5:
15         n +=1
16         con.send(n)
17         con2.send(n)
18         print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
19 
20 
21 if __name__ == '__main__':
22     con = consumer("c1")
23     con2 = consumer("c2")
24     p = producer()
复制代码
Greenlet
复制代码
 1 from greenlet import greenlet
 2   
 3 def test1():
 4     print 12
 5     gr2.switch()
 6     print 34
 7     gr2.switch()
 8   
 9   
10 def test2():
11     print 56
12     gr1.switch()
13     print 78
14   
15 gr1 = greenlet(test1)
16 gr2 = greenlet(test2)
17 gr1.switch()
复制代码

Gevent

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

复制代码
 1 import gevent
 2  
 3 def foo():
 4     print('Running in foo')
 5     gevent.sleep(0)
 6     print('Explicit context switch to foo again')
 7  
 8 def bar():
 9     print('Explicit context to bar')
10     gevent.sleep(0)
11     print('Implicit context switch back to bar')
12  
13 gevent.joinall([
14     gevent.spawn(foo),
15     gevent.spawn(bar),
16 ])
复制代码

同步与异步的性能区别

复制代码
 1 import gevent
 2  
 3 def task(pid):
 4     """
 5     Some non-deterministic task
 6     """
 7     gevent.sleep(0.5)
 8     print('Task %s done' % pid)
 9  
10 def synchronous():
11     for i in range(1,10):
12         task(i)
13  
14 def asynchronous():
15     threads = [gevent.spawn(task, i) for i in range(10)]
16     gevent.joinall(threads)
17  
18 print('Synchronous:')
19 synchronous()
20  
21 print('Asynchronous:')
22 asynchronous()
复制代码

上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,后者阻塞当前流程,并执行所有给定的greenlet。执行流程只会在 所有greenlet执行完后才会继续向下走。  

遇到IO阻塞时会自动切换任务

复制代码
 1 from gevent import monkey; monkey.patch_all()
 2 import gevent
 3 from  urllib.request import urlopen
 4  
 5 def f(url):
 6     print('GET: %s' % url)
 7     resp = urlopen(url)
 8     data = resp.read()
 9     print('%d bytes received from %s.' % (len(data), url))
10  
11 gevent.joinall([
12         gevent.spawn(f, 'https://www.python.org/'),
13         gevent.spawn(f, 'https://www.yahoo.com/'),
14         gevent.spawn(f, 'https://github.com/'),
15 ])
复制代码

通过gevent实现单线程下的多socket并发

server side

复制代码
 1 import sys
 2 import socket
 3 import time
 4 import gevent
 5  
 6 from gevent import socket,monkey
 7 monkey.patch_all()
 8 def server(port):
 9     s = socket.socket()
10     s.bind(('0.0.0.0', port))
11     s.listen(500)
12     while True:
13         cli, addr = s.accept()
14         gevent.spawn(handle_request, cli)
15 def handle_request(s):
16     try:
17         while True:
18             data = s.recv(1024)
19             print("recv:", data)
20             s.send(data)
21             if not data:
22                 s.shutdown(socket.SHUT_WR)
23  
24     except Exception as  ex:
25         print(ex)
26     finally:
27  
28         s.close()
29 if __name__ == '__main__':
30     server(8001)
复制代码

client side

复制代码
 1 import socket
 2  
 3 HOST = 'localhost'    # The remote host
 4 PORT = 8001           # The same port as used by the server
 5 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6 s.connect((HOST, PORT))
 7 while True:
 8     msg = bytes(input(">>:"),encoding="utf8")
 9     s.sendall(msg)
10     data = s.recv(1024)
11     #print(data)
12  
13     print('Received', repr(data))
14 s.close()
复制代码

 

转载--> http://www.cnblogs.com/wupeiqi/articles/5040827.html

 

分类: Python
0
0
«上一篇: Python学习之路--面向对象
»下一篇: Python学习之路--Socket