Python 第八篇:异常处理、Socket语法、SocketServer实现多并发、进程和线程、线程锁、GIL、Event、信号量、进程间通信

本节内容:python

异常处理、Socket语法、SocketServer实现多并发、进程和线程、线程锁、GIL、Event、信号量、进程间通信、生产者消费者模型、队列Queue、multiprocess实例git

异常处理、红绿灯、吃包子实例程序员

 

一:异常处理:github

异常是由于程序出现了错误而在正常的控制流觉得采起的行为,当python检测到一个异常的时候,解释器就会支出当前流已经没法继续下去,这时候就出现了异常,从python 1.5开始,全部的标准异常都是实用类实现的, Python的异常处理能力是很强大的,可向用户准确反馈出错信息。在Python中,异常也是对象,可对它进行操做。全部异常都是基类Exception的成员。全部异常都从基类Exception继承,并且都在exceptions模块中定义。Python自动将全部异常名称放在内建命名空间中,因此程序没必要导入exceptions模块便可使用异常。一旦引起并且没有捕捉SystemExit异常,程序执行就会终止。若是交互式会话遇到一个未被捕捉的SystemExit异常,会话就会终止。数组

while True:
    try: #正常执行的代码
        a = input("num1:")
        b = input("num2:")
        c = range(10)

        num1 = int(a)
        num2 = int(b)
        result = num1 + num2
        print(c[11])
    except ValueError as e: #执行捕获的异常名称 ValueError,并将抛出的错误信息保存到e以备调用:
        print("ValueError:",e)
    except IndentationError as e:
        print("index error:",2)
    except IndexError as e:
        print(c[4])
    except KeyboardInterrupt as e:
        print("ctrl + c")
    except Exception as e:
        print("捕获到为止异常:")
        print(e)
    finally:
        print("xxxx")

自定义异常和断言:服务器

断言:assert  必须知足的条件,知足继续向下,不知足跳出,和if ---> else语句有点类似:数据结构

while True:
    try:
        a = input("num1:")
        b = input("num2:")
        c = range(10)

        num1 = int(a)
        num2 = int(b)
        assert  num1 > num2  #必须知足的条件,知足继续向下,不知足跳出
        result = num1 + num2
        print(result)
        print(c[4])
    except ValueError as e: #值错误
        print("ValueError:",e)
    except IndexError as e: #索引错误,能够更换索引
        print(c[4])
    except KeyboardInterrupt as e: #
        print("ctrl + c")

    except Exception as e:
        print("捕获除ctrl+c/语法错误/等特殊异常之外的全部异常:")
        print(e)
    else:
        print("没有出现异常即代码执行成功3才执行")
    finally:
        print("无论有没有异常都执行")

 举几个小例子,看一下异常捕获怎么使用的:多线程

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

str_input = 'jack'
number = int(str_input)
print(number)

执行结果:
Traceback (most recent call last):
  File "C:/Users/zhang/PycharmProjects/S12-python3/day8/test/test.py", line 5, in <module>
    number = int(str_input)
ValueError: invalid literal for int() with base 10: 'jack'
未使用异常捕获举例:
try:
    str_input = 'jack'
    number = int(str_input)
    print(number)
except Exception as e:
    print("\033[32;1m出现错误以下\033[0m")
    print(e)

执行结果:
出现错误以下
invalid literal for int() with base 10: 'jack'
使用一次捕获举例:
try:
    #正常逻辑代码
    user_input = input("\033[32;1m请输入数字:\033[0m")
    number = int(user_input)
except Exception as e:  #这个e是对象Exception类建立的!Exception这里面封装了你上面逻辑块出现问题的全部错误
    #逻辑代码出现错误,这里的代码块就是若是上面的代码出现问题以后执行这个代码块的内容
    print(e) #若是这个e你不想要,能够本身定义
    #这里也能够记录日志,把错误的详细记录,错误详细在e里了!

执行结果1,输入整数:
请输入数字:123
123

执行结果2,输入非法的字符串:
请输入数字:1as
invalid literal for int() with base 10: '1as'
再次举例异常捕获

 

2,socket语法知识:并发

socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) #获取要链接的对端主机地址
sk.bind(address) #s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

sk.listen(backlog) #开始监听传入链接。backlog指定在拒绝链接以前,能够挂起的最大链接数量,backlog等于5,表示内核已经接到了链接请求,但服务器尚未调用accept进行处理的链接个数最大为5,这个值不能无限大,由于要在内核中维护链接队列

sk.setblocking(bool) #是否阻塞(默认True),若是设置False,那么accept和recv时一旦无数据,则报错。

sk.accept() #接受链接并返回(conn,address),其中conn是新的套接字对象,能够用来接收和发送数据。address是链接客户端的地址,接收TCP 客户的链接(阻塞式)等待链接的到来

sk.connect(address) #链接到address处的套接字。通常,address的格式为元组(hostname,port),若是链接出错,返回socket.error错误。

sk.connect_ex(address) #同上,只不过会有返回值,链接成功时返回 0 ,链接失败时候返回编码,例如:10061

sk.close() #关闭套接字

sk.recv(bufsize[,flag]) #接受套接字的数据。数据以字符串形式返回,bufsize指定最多能够接收的数量。flag提供有关消息的其余信息,一般能够忽略。

sk.recvfrom(bufsize[.flag]) #与recv()相似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sk.send(string[,flag]) #将string中的数据发送到链接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容所有发送。

sk.sendall(string[,flag]) #将string中的数据发送到链接的套接字,但在返回以前会尝试发送全部数据。成功返回None,失败则抛出异常,内部经过递归调用send,将全部内容发送出去。

sk.sendto(string[,flag],address) #将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

sk.settimeout(timeout) #设置套接字操做的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。通常,超时期应该在刚建立套接字时设置,由于它们可能用于链接的操做(如 client 链接最多等待5s )

sk.getpeername() #返回链接套接字的远程地址。返回值一般是元组(ipaddr,port)。

sk.getsockname() #返回套接字本身的地址。一般是一个元组(ipaddr,port)

sk.fileno() #套接字的文件描述符

socket.sendfile(file, offset=0, count=None) #发送文件 ,但目前多数状况下并没有什么卵用
socket 基础语法回顾

 

3,socketserver 实现多并发:在server端启动多线程进行监听,有客户请求就随时启动多线程与用户创建链接,socketserver内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每一个客户端请求链接到服务器时,Socket服务端都会在服务器是建立一个“线程”或者“进 程” 专门负责处理当前客户端的全部请求。app

socket server 和 select & epoll 仍是不太同样他的本质是:客户端第一次连接的时候,只要一进来,我服务端有个while循环为你建立一个
线程和进程,客户端就和服务端直接建立通讯,之后传送数据什么的就不会经过server端了,直接他俩经过线程或者进程通讯就能够了!

若是在多进程的时候,client1和client2他们同时传输10G的文件都是互相不影响!
若是在多线程的时候,python中的多线程,在同一时间只有一个线程在工做,他底层会自动进行上下文切换,client1传一点,client2传一点。

知识回顾:
python中的多线程,有一个GIL在同一时间只有一个线程在工做,他底层会自动进行上下文切换.
这样会致使python的多线程效率会很低,也就是人们常常说的python多线程问题
socket server 简明介绍

举例:

import  socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        print("New Conn:",self.client_address)
        while True:
            data = self.request.recv(1024)
            if not  data:
                break
            print("Client Says:",data.decode())
            self.request.send(data)
            self.request.send(bytes("本次数发送完毕","utf8"))


if __name__ == "__main__":
    Host,Port = "localhost",9003
    server = socketserver.ThreadingTCPServer((Host,Port),MyTCPHandler)
    server.serve_forever()
socketserver服务器端
import  socket
ip_port = ("127.0.0.1",9003)
sk = socket.socket()
sk.connect(ip_port)

while True:
    aaa = input(">>:")
    sk.send(bytes(aaa,"utf8"))
    server_reply = sk.recv(4096)
    print(server_reply.decode())
    data = sk.recv(1024)
    print(data.decode())
socketserver 客户端

客户端和服务器端第一次链接后,数据通信就经过线程或进程进行数据交换(红色箭头)

内部调用流程为:

  • 启动服务端程序
  • 执行 TCPServer.__init__ 方法,建立服务端Socket对象并绑定 IP 和 端口
  • 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给self.RequestHandlerClass
  • 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
  • 当客户端链接到达服务器
  • 执行 ThreadingMixIn.process_request 方法,建立一个 “线程” 用来处理请求
  • 执行 ThreadingMixIn.process_request_thread 方法
  • 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass()  即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)

 

 4.进程和线程:

 ThreadingTCPServer实现的Soket服务器内部会为每一个client建立一个 “线程”,该线程用来和客户端进行交互:

一、ThreadingTCPServer基础

使用ThreadingTCPServer:

  • 建立一个继承自 SocketServer.BaseRequestHandler 的类
  • 类中必须定义一个名称为 handle 的方法
  • 启动ThreadingTCPServer

什么是线程(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).
thread 英文解释

什么是进程(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 GIL(Global Interpreter Lock):

上面的核心意思就是,不管你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只容许一个线程运行,擦。。。,那这还叫什么多线程呀?

首先须要明确的一点是GIL并非Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就比如C++是一套语言(语法)标准,可是能够用不一样的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也同样,一样一段代码能够经过CPython,PyPy,Psyco等不一样的Python执行环境来执行。像其中的JPython就没有GIL。然而由于CPython是大部分环境下默认的Python执行环境。因此在不少人的概念里CPython就是Python,也就想固然的把GIL归结为Python语言的缺陷。因此这里要先明确一点:GIL并非Python的特性,Python彻底能够不依赖于GIL。

这篇文章透彻的剖析了GIL对python多线程的影响,强烈推荐看一下:http://www.dabeaz.com/python/UnderstandingGIL.pdf 

 

5,threading实例,有两种调用方式,以下:

import threading
import time
 
def sayhi(num): #定义每一个线程要运行的函数
    print("running on number:%s" %num)
    time.sleep(3)
 
if __name__ == '__main__':
    t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
    t2 = threading.Thread(target=sayhi,args=(2,)) #生成另外一个线程实例
    t1.start() #启动线程
    t2.start() #启动另外一个线程

    print(t1.getName()) #获取线程名
    print(t2.getName())
方式一:直接调用
import threading
import time
 
class MyThread(threading.Thread):
    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num = num
 
    def run(self):#定义每一个线程要运行的函数
        print("running on number:%s" %self.num)
        time.sleep(3)
 
if __name__ == '__main__':
    t1 = MyThread(1)
    t2 = MyThread(2)
    t1.start()
    t2.start()
方式二:继承式调用

 

更多线程操做方法:

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

6,线程锁、GIL、Event、信号量:

线程锁(互斥锁Mutex)

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

import time
import threading
 
def addNum():
    global num #在每一个线程中都获取这个全局变量
    print('--get num:',num )
    time.sleep(1)
    num  -=1 #对此公共变量进行-1操做
 
num = 100  #设定一个共享变量
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('final num:', num )
Python 2 未加锁

正常来说,这个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上的结果老是正确的,多是自动加了锁

加锁版本:

import time
import threading
 
def addNum():
    global num #在每一个线程中都获取这个全局变量
    print('--get num:',num )
    time.sleep(1)
    lock.acquire() #修改数据前加锁
    num  -=1 #对此公共变量进行-1操做
    lock.release() #修改后释放
 
num = 100  #设定一个共享变量
thread_list = []
lock = threading.Lock() #生成全局锁
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)
 
for t in thread_list: #等待全部线程执行完毕
    t.join()
 
print('final num:', num )
Python 2加锁版本

RLock(递归锁)

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

import threading,time
 
def run1():
    print("grab the first part data")
    lock.acquire()
    global num
    num +=1
    lock.release()
    return num
def run2():
    print("grab the second part data")
    lock.acquire()
    global  num2
    num2+=1
    lock.release()
    return num2
def run3():
    lock.acquire()
    res = run1()
    print('--------between run1 and run2-----')
    res2 = run2()
    lock.release()
    print(res,res2)
 
 
if __name__ == '__main__':
 
    num,num2 = 0,0
    lock = threading.RLock()
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()
 
while threading.active_count() != 1:
    print(threading.active_count())
else:
    print('----all threads done---')
    print(num,num2)
Rlock:递归锁

Semaphore(信号量)

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

import threading,time

def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s\n" %n)
    semaphore.release()

if __name__ == '__main__':

    num= 0
    semaphore  = threading.BoundedSemaphore(2) #最多容许5个线程同时运行
    for i in range(20):
        t = threading.Thread(target=run,args=(i,))
        t.start()

while threading.active_count() != 1:
    pass #print threading.active_count()
else:
    print('----all threads done---')
    print(num)
每次启动两个线程

event

他的做用就是:用主线程控制子线程合适执行,他可让子线程停下来,也可让线程继续:

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

import  threading,time,random

def light():
    if not  event.isSet():
        event.set() #设置为绿灯
    count = 0
    while True:
        if count < 10:
            print("\033[42;1m=====绿灯=====\033[0m")
        elif count < 13:
            print("\033[43;1m======黄灯======\033[0m")
        elif count < 20:
            if event.isSet():
                event.clear()
            print("\033[44;0m=======红灯=====\033[0m")
        else:
            count = 0
            event.set()
        time.sleep(1)
        count += 1

def car(n):
    while True:
        time.sleep(random.randrange(12)) 
        if event.isSet(): #lvdeg
            print("car %s is runing" % n)
        else:
            print("car %s is wating for the red light" % n)

if __name__ == "__main__":
    event = threading.Event()
    Light = threading.Thread(target=light)
    Light.start()
    for i in range(3):
        t = threading.Thread(target=car,args=[i,])
        t.start()

 六、Python进程:

from multiprocessing import Process
import threading
import time
  
def foo(i):
    print 'say hi',i
  
for i in range(10):
    p = Process(target=foo,args=(i,))
    p.start()

注意:因为进程之间的数据须要各自持有一份,因此建立进程须要的很是大的开销。而且python不能再Windows下建立进程!

而且在使用多进程的时候,最好是建立多少个进程?:和CPU核数相等

默认的进程之间相互是独立,若是想让进程之间数据共享,就得有个特殊的数据结构,这个数据结构就能够理解为他有穿墙的功能
若是你能穿墙的话两边就均可以使用了
使用了3种方法:

默认的进程没法进行数据共享:

#!/usr/bin/env python
#coding:utf-8
#author Zhang Shijie
from multiprocessing import Process
from multiprocessing import Manager

import time

li = []

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

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

print('ending',li)

使用特殊的数据类型,来进行穿墙:

默认的进程之间相互是独立,若是想让进程之间数据共享,就得有个特殊的数据结构,这个数据结构就能够理解为他有穿墙的功能
若是你能穿墙的话两边就均可以使用了
使用了3种方法


第一种方法:

#经过特殊的数据结构:数组(Array)

from multiprocessing import Process,Array

#建立一个只包含数字类型的数组(python中叫列表)
#而且数组是不可变的,在C,或其余语言中,数组是不可变的,以后再python中数组(列表)是能够变得
#固然其余语言中也提供可变的数组
#在C语言中数组和字符串是同样的,若是定义一个列表,若是能够增长,那么我须要在你内存地址后面再开辟一块空间,那我给你预留多少呢?
#在python中的list可能用链表来作的,我记录了你前面和后面是谁。   列表不是连续的,数组是连续的

'''
上面不是列表是“数组"数组是不可变的,附加内容是为了更好的理解数组!
'''

temp = Array('i', [11,22,33,44]) #这里的i是C语言中的数据结构,经过他来定义你要共享的内容的类型!点进去看~
 
def Foo(i):
    temp[i] = 100+i
    for item in temp:
        print i,'----->',item
 
for i in range(2):
    p = Process(target=Foo,args=(i,))
    p.start()
    
第二种方法:
#方法二:manage.dict()共享数据
from multiprocessing import Process,Manager  #这个特殊的数据类型Manager
 
manage = Manager()
dic = manage.dict() #这里调用的时候,使用字典,这个字典和我们python使用方法是同样的!
 
def Foo(i):
    dic[i] = 100+i
    print dic.values()
 
for i in range(2):
    p = Process(target=Foo,args=(i,))
    p.start()
    p.join()

OK那么问题来了,既然进程之间能够进行共享数据,若是多个进程同时修改这个数据是否是就会形成脏数据?是否是就得须要锁!

进程的锁和线程的锁使用方式是很是同样的知识他们是用的类是在不一样地方的

 

 进程池

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

进程池中有两个方法:

  • apply
  • apply_async
from  multiprocessing import Process,Pool
import time

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

def Bar(arg):
    print(arg)

pool = Pool(5) #建立一个进程池
#print pool.apply(Foo,(1,))#去进程池里去申请一个进程去执行Foo方法
#print pool.apply_async(func =Foo, args=(1,)).get()

for i in range(10):
    pool.apply_async(func=Foo, args=(i,),callback=Bar)

print('end')
pool.close()
pool.join()#进程池中进程执行完毕后再关闭,若是注释,那么程序直接关闭。

'''
apply 主动的去执行
pool.apply_async(func=Foo, args=(i,),callback=Bar) 至关于异步,当申请一个线程以后,执行FOO方法就无论了,执行完以后就在执行callback ,当你执行完以后,在执行一个方法告诉我执行完了
callback 有个函数,这个函数就是操做的Foo函数的返回值!
'''

 

协程:

首先要明确,线程和进程都是系统帮我们开辟的,无论是thread仍是process他内部都是调用的系统的API
而对于协程来讲它和系统毫无关系!
他就和程序员有关系,对于线程和进程来讲,调度是由CPU来决定调度的!
对于协程来讲,程序员就是上帝,你想让谁执行到哪里他就执行到哪里

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

适用场景:其实在其余语言中,协程的实际上是意义不大的多线程便可已解决I/O的问题,可是在python由于他有GIL(Global Interpreter Lock 全局解释器锁 )在同一时间只有一个线程在工做,因此:若是一个线程里面I/O操做特别多,协程就比较适用

greenlet:

收先要明确,线程和进程都是系统帮我们开辟的,无论是thread仍是process他内部都是调用的系统的API
而对于协程来讲它和系统毫无关系!
他就和程序员有关系,对于线程和进程来讲,是否是有CPU来决定调度的!
对于协程来讲,程序员就是上帝,你想让谁执行到哪里他就执行到哪里

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
 
from greenlet import greenlet
 
 
def test1():
    print 12
    gr2.switch()#切换到协程2执行
    print 34 #2切回来以后,在这里和yield相似
    gr2.switch() 
 
 
def test2():
    print 56
    gr1.switch()#上面执行了一句,在切换到协程1里去了
    print 78
 
gr1 = greenlet(test1) #建立了一个协程
gr2 = greenlet(test2)

gr1.switch() #执行test1 

'''
比I/O操做,若是10个I/O,我程序从上往下执行,若是同时发出去了10个I/O操做,那么返回的结果若是同时回来了2个
,是否是就节省了不少时间?

若是一个线程里面I/O操做特别多,使用协程是否是就很是适用了!

若是一个线程访问URL经过协程来作,协程告诉它你去请求吧,而后继续执行,可是若是不用协程就得等待第一个请求完毕以后返回以后才
继续下一个请求。

协程:把一个线程分红了多个协程操做,每一个协程作操做
多线程:是把每个操做,分为多个线程作操做,可是python中,在同一时刻只能有一个线程操做,而且有上下文切换。可是若是上下文切换很是频繁的话
是很是耗时的,但对于协程切换就很是轻便了~
greenlet

协程就是对线程的分片,上面的例子须要手动操做可能用处不是很大了解原理,看下面的例子:

上面的greenlet是须要认为的制定调度顺序的,因此又出了一个gevent他是对greenlet功能进行封装

遇到I/O自动切换:

from gevent import monkey; monkey.patch_all()
import gevent
import urllib2

def f(url):
    print('GET: %s' % url)
    resp = urllib2.urlopen(url) #当遇到I/O操做的时候就会调用协程操做,而后继续往下走,而后这个协程就卡在这里等待数据的返回
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))

gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/'),  #这里的f是调用这个方法,第二个是调用方的参数
        gevent.spawn(f, 'https://www.yahoo.com/'),
        gevent.spawn(f, 'https://github.com/'),
]) 

'''
gevent.spawn(f, 'https://www.python.org/'),  #这里的f是调用这个方法,第二个是调用方的参数

当函数f里的代码遇到I/O操做的时候,函数就卡在哪里等待数据的返回,可是协程不会等待而是继续操做!
'''
相关文章
相关标签/搜索