Python自動化之select、greenlet和gevent和事件驅動模型初探

進程、線程和協程的區別

進程擁有本身獨立的堆和棧,既不共享堆,亦不共享棧,進程由操做系統調度。python

線程擁有本身獨立的棧和共享的堆,共享堆,不共享棧,線程亦由操做系統調度(標準線程是的)。git

協程和線程同樣共享堆,不共享棧,協程由程序員在協程的代碼裏顯示調度程序員

協程和線程的區別是:協程避免了無心義的調度,由此能夠提升性能,但也所以,程序員必須本身承擔調度的責任,同時,協程也失去了標準線程使用多CPU的能力。github

Greenlet模塊

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

from greenlet import greenlet
  
  
def test1():
    print 12
    gr2.switch()
    print 34
    gr2.switch()
  
  
def test2():
    print 56
    gr1.switch()
    print 78
  
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

每個greenlet其實就是一個函數,以及保存這個函數執行時的上下文.對於函數來講上下文也就是其stack。
greenlet須要你本身來處理線程切換, 就是說,你須要本身指定如今執行哪一個greenlet再執行哪一個greenlet。編程

Gevent(第三方庫)

import gevent
 
def foo():
    print('Running in foo')
    gevent.sleep(0)
    print('Explicit context switch to foo again')
 
def bar():
    print('Explicit context to bar')
    gevent.sleep(0)
    print('Implicit context switch back to bar')
 
gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
])

gevent是第三方庫,經過greenlet實現協程,其基本思想是:
  當一個greenlet遇到IO操做時,好比訪問網絡,就自動切換到其餘的greenlet,等到IO操做完成,再在適當的時候切換回來繼續執行。因爲IO操做很是耗時,常常使程序處於等待狀態,有了gevent爲咱們自動切換協程,就保證總有greenlet在運行,而不是等待IO。
gevent.sleep()來互相切換,實際上gevent是能夠自動切換的。網絡

遇到IO自動切換任務併發

from gevent import monkey; monkey.patch_all()
import gevent
from  urllib.request import urlopen
 
def f(url):
    print('GET: %s' % url)
    resp = urlopen(url)
    data = resp.read()
    print('%d bytes received from %s.' % (len(data), url))
 
gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/'),
        gevent.spawn(f, 'https://www.yahoo.com/'),
        gevent.spawn(f, 'https://github.com/'),
])

經過gevent實現單線程下的多socket併發socket

服務端函數

import sys
import socket
import time
import gevent
 
from gevent import socket,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)

客戶端性能

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()

事件驅動模型

目前大部分的UI編程都是事件驅動模型,如不少UI平臺都會提供onClick()事件,這個事件就表明鼠標按下事件。事件驅動模型大致思路以下:

  1. 有一個事件(消息)隊列;
  2. 鼠標按下時,往這個隊列中增長一個點擊事件(消息);
  3. 有個循環,不斷從隊列取出事件,根據不一樣的事件,調用不一樣的函數,如onClick()、onKeyDown()等;
  4. 事件(消息)通常都各自保存各自的處理函數指針,這樣,每一個消息都有獨立的處理函數;
相關文章
相關標籤/搜索