xone

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

进程

cpu要执行多任务,出现了进程;
进程是一个程序需要运行所需的资源的集合。
每个进程数据是独立的
每个进程里至少有一个线程。
进程里可以有多个线程
线程数据共享的
 
一个进程的多个线程可以充分利用多核cpu,即可以跑在多核上。
 
mutliprocessing
    pipe
    Queue
    上面两个实现的是进程间的数据传递,通讯。
 
    manager:实现了多进程间的数据共享
    进程间共享数据的代价是高昂的,所以要尽量避免进程间的数据共享
    线程间的数据本来就是共享的,
    线程要修改同一份数据,必须加锁,互斥锁mutex。
 
    event
            线程间交互。
    
 
生产者消费者模型
        解耦
        提高程序的运行效率
        
        queue
                FIFO:排队
                LIFO:超市里的菜
                优先级queue:银行里金卡
 
适用场景
线程:IO密集型,socket 爬虫,web
进程:cpu运算密集型,金融分析
 

协程

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

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

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

 

协程的好处:

  • 无需线程上下文切换的开销
  • 无需原子操作锁定及同步的开销
    •   "原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
  • 方便切换控制流,简化编程模型
  • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

 

缺点:

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

Greenlet

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

def test1():
    print(12)
    time.sleep(1)
    gr2.switch()
    print(34)

    gr2.switch()


def test2():
    print(56)
    gr1.switch()
    print(78)


gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

以上代码运行结果

12
56
34
78

 

并没有解决遇到IO自动切换。
 

Gevent

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

def func1():
    print('\033[31;1m李闯在跟海涛搞...\033[0m')
    gevent.sleep(4)
    print('\033[31;1m李闯又回去继续跟海涛搞...\033[0m')

def func2():
    gevent.sleep(0.5)
    print('\033[32;1m李闯切换到了跟海龙搞...\033[0m')
    gevent.sleep(2)
    print('\033[32;1m李闯搞完了海涛,回来继续跟海龙搞...\033[0m')

def func3():
    print("33333")
    gevent.sleep(1)
    print("4444")

gevent.joinall([
    gevent.spawn(func1),
    gevent.spawn(func2),
    gevent.spawn(func3),
])

 以上代码运行结果

李闯在跟海涛搞...
33333
李闯切换到了跟海龙搞...
4444
李闯搞完了海涛,回来继续跟海龙搞...
李闯又回去继续跟海涛搞...

 

同步与异步的性能区别 

import gevent

def task(pid):
    """
    Some non-deterministic task
    """
    gevent.sleep(0.5)
    print('Task %s done' % pid)

def synchronous():
    for i in range(1, 10):
        task(i)

def asynchronous():
    #threads = [gevent.spawn(task, i) for i in range(10)]
    threads  = []
    for i in range(10):
        threads.append(gevent.spawn(task,i))

    gevent.joinall(threads)

print('Synchronous:')
synchronous()

print('Asynchronous:')
asynchronous()

 以上代码运行结果

Synchronous:
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Task 5 done
Task 6 done
Task 7 done
Task 8 done
Task 9 done
Asynchronous:
Task 0 done
Task 9 done
Task 8 done
Task 7 done
Task 6 done
Task 5 done
Task 4 done
Task 3 done
Task 2 done
Task 1 done

 

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

import gevent
from gevent import monkey   #将url里面所有的操作改成异步的模式,即不堵塞的模式
monkey.patch_all()
from  urllib.request import urlopen
import time

def pa_web_page(url):
    print("GET url",url)
    req = urlopen(url)
    data =req.read()
    # print(data)
    print('%d bytes received from %s.' % (len(data), url))

t_start = time.time()
pa_web_page("http://www.autohome.com.cn/beijing/")
pa_web_page("http://www.xiaohuar.com/")
print("time cost:",time.time()-t_start)

t2_start = time.time()
gevent.joinall([
        #gevent.spawn(pa_web_page, 'https://www.python.org/'),
        gevent.spawn(pa_web_page, 'http://www.autohome.com.cn/beijing/'),
        gevent.spawn(pa_web_page, 'http://www.xiaohuar.com/'),
        #gevent.spawn(pa_web_page, 'https://github.com/'),
])
print("time cost t2:",time.time()-t2_start)

 

以上代码执行结果
1077321 bytes received from http://www.autohome.com.cn/beijing/.
GET url http://www.xiaohuar.com/
49847 bytes received from http://www.xiaohuar.com/.
time cost: 0.4413177967071533
GET url http://www.autohome.com.cn/beijing/
GET url http://www.xiaohuar.com/
49847 bytes received from http://www.xiaohuar.com/.
1077321 bytes received from http://www.autohome.com.cn/beijing/.
time cost t2: 0.23272156715393066

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

server side

import sys
import socket
import time
import gevent

from gevent import monkey

monkey.patch_all()

def server(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli)

def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024)
            print("recv:", data)
            conn.send(data)
            if not data:
                conn.shutdown(socket.SHUT_WR)

    except Exception as  ex:
        print(ex)
    finally:
        conn.close()

if __name__ == '__main__':
    server(8001)

 

 
client side 
 
import socket

HOST = 'localhost'  # The remote host
PORT = 8001  # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"), encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    # print(data)

    print('Received', repr(data))
s.close()

 用threading并发100个连接

import socket
import threading

def sock_conn():

    client = socket.socket()

    client.connect(("localhost",8001))
    count = 0
    while True:
        #msg = input(">>:").strip()
        #if len(msg) == 0:continue
        client.send( ("hello %s" %count).encode("utf-8"))

        data = client.recv(1024)

        print("[%s]recv from server:" % threading.get_ident(),data.decode()) #结果
        count +=1
    client.close()

for i in range(5000):
    t = threading.Thread(target=sock_conn)
    t.start()

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted on 2016-12-19 18:02  周小百  阅读(716)  评论(0编辑  收藏  举报