python多進程與協程

一、進程的概念

什麼是進程—>CPU在同一時刻只能處理一個任務,只是由於cpu執行速度很快。
cpu在各個任務之間來回的進行切換。
進程的概念:正在進行的一個過程或者說一個任務,而負責執行任務的則是CPU,進程自己是
一個抽象的概念,即進程就是一個過程、一個任務。
CPU描述的是一個程序的執行過程.
進程之間是如何作到併發的:CPU在各個任務之間來回的進行切換,並在切換的過程中保存當前
進程的執行狀態(保存蛋糕的執行過程)。
進程與程序的區別:程序至關於菜譜,而進程至關於作菜的整個過程。
須要強調的是:同一個程序執行兩次(雙擊),那也是兩個進程,好比打開暴風影音,雖然都是同一個軟件,可是一個能夠播放a,一個能夠播放b.
核的概念:https://zhidao.baidu.com/question/541410131.html
處理器,就是說有幾個處理器。。。也就說一個CPU裏面會有幾個處理器,這樣就能夠同時處理幾個要求了。。。html

二、並行與併發的區別

不管是並行仍是併發,在用戶看來都是同時運行的,不論是進程仍是線程,都只是一個任務而已,
真正幹活的是CPU,CPU來作這些任務,而一個cpu(單核)同一時刻只能執行一個任務。
並行:多個任務同時運行,只有具有多個cpu才能實現並行,含有幾個cpu,也就意味着在同一時刻能夠執行幾個任務。
併發:是僞並行,即看起來是同時運行的,其實是單個CPU在多道程序之間來回的進行切換。python

三、同步與異步的概念

同步就是指一個進程在執行某個請求的時候,若該請求須要一段時間才能返回信息,那麼這個進程將會一直等待下去,直到收到返回信息才繼續執行下去。
異步是指進程不須要一直等下去,而是繼續執行下面的操做,無論其餘進程的狀態。當有消息返回時系統會通知進行處理,這樣能夠提升執行的效率。
打電話的過程就是同步通訊,發短信時就是異步通訊。windows

四、進程建立的方式

用戶建立出來的全部進程都是由操做系統負責的,所以不管是哪種建立進程的方式,實際上都是調用操做系統的接口建立的,進程的切換都是由操做系統控制的。
不管哪種建立進程的方式,新進程的建立都是由一個已經存在的進程執行了一個用於建立進程的系統調用而建立的。緩存

五、父進程和子進程之間的關係

子進程建立後,父進程和子進程有各自不一樣的地址空間,多道技術要求物理層面實現進程之間內存的
隔離,任何一個進程在其地址空間的修改都不會影響到另一個進程。
注意:子進程和父進程之間是能夠有隻讀的共享的內存區域的。
進程與進程之間數據(資源)是隔離的,兩個進程之間能夠基於管道這種方式進行通訊。在Unix當中,是含有進程層次的概念的,可是在windows當中,是沒有進程層次的概念的,全部的進程都是地位相同的。
在Linux當中,每啓動一個命令,都會啓動一個進程。網絡

六、線程的概念

一個進程裏面至少有一個控制線程,進程的概念只是一種抽象的概念,真正在CPU上面調度的是進程
裏面的線程,就比如真正在地鐵這個進程裏面工做的其實是地鐵裏面的線程,北京地鐵裏面至少要有
一個線程,線程是真正幹活的,線程用的是進程裏面包含的一堆資源,線程僅僅是一個調度單位,不包含資源。多線程

七、 何時須要開啓多個線程?

何時須要開啓多個線程:一個進程裏面的多個線程共享這個進程裏面的資源,所以若是多個任務共享同一塊資源的時候,須要開啓多個線程。
多線程指的是,在一個進程中開啓多個線程,簡單的說:若是多個任務共用同一個資源空間,那麼必須在一個進程內開啓多個線程。併發

八、一個進程裏面須要包含多個線程?

一個進程這個任務裏面可能對應多個分任務,若是一個進程裏面只開啓一個線程的話,多個分任務之間其實是串行的執行效果,即一個程序裏面只含有一條執行路徑。異步

九、多線程和多進程的關係

對於計算密集型應用,應該使用多進程;對於IO密集型應用,應該使用多線程。
線程的建立比進程的建立開銷小的多。函數

'''
about what
'''
import multiprocessing

import time


def func(arg):
    pname = multiprocessing.current_process().name
    pid = multiprocessing.current_process().pid
    print("當前進程ID=%d,name=%s" % (pid, pname))

    for i in range(5):
        print(arg)
        time.sleep(1)
        
if __name__ == "__main__":
    pname = multiprocessing.current_process().name
    pid = multiprocessing.current_process().pid
    print("當前進程ID=%d,name=%s" % (pid, pname))

    p = multiprocessing.Process(target=func, args=("hello",))
    # p = multiprocessing.Process(target=func,name="勞資的隊伍",args=("hello",))
    p.daemon = True  # 設爲【守護進程】(隨主進程的結束而結束)
    p.start()

    while True:
        print("子進程是否活着?", p.is_alive())
        time.sleep(1)

    print("main over")

協程

協程,又稱微線程,纖程。英文名Coroutine。性能

協程是啥 ??

首先咱們得知道協程是啥?協程其實能夠認爲是比線程更小的執行單元。爲啥說他是一個執行單元,由於他自帶CPU上下文。這樣只要在合適的時機,咱們能夠把一個協程切換到另外一個協程,只要這個過程當中保存或恢復 CPU上下文那麼程序仍是能夠運行的。

通俗的理解:在一個線程中的某個函數,能夠在任何地方保存當前函數的一些臨時變量等信息,而後切換到另一個函數中執行,注意不是經過調用函數的方式作到的,而且切換的次數以及何時再切換到原來的函數都由開發者本身肯定。

協程和線程差別

最大的優點就是協程極高的執行效率,由於子程序切換不是線程切換,而是由程序自身控制,所以,沒有線程切換的開銷線程切換從系統層面遠不止保存和恢復 CPU上下文這麼簡單。操做系統爲了程序運行的高效性每一個線程都有本身緩存Cache等等數據,操做系統還會幫你作這些數據的恢復操做。因此線程的切換很是耗性能。可是協程的切換隻是單純的操做CPU的上下文,因此一秒鐘切換個上百萬次系統都抗的住。

第二大優點就是不須要多線程的鎖機制,由於只有一個線程,也不存在同時寫變量衝突。

二、使用協程

1.使用greenlet + switch實現協程調度

'''
使用greenlet + switch實現協程調度
'''
from greenlet import greenlet

import time

def func1():
    print("開門走進衛生間")
    time.sleep(3)
    gr2.switch()  # 把CPU執行權交給gr2

    print("飛流直下三千尺")
    time.sleep(3)
    gr2.switch()
    pass

def func2():
    print("一看拖把放旁邊")
    time.sleep(3)
    gr1.switch()

    print("疑是銀河落九天")
    pass

if __name__ == '__main__':
    gr1 = greenlet(func1)
    gr2 = greenlet(func2)
    gr1.switch()  # 把CPU執行權先給gr1
    pass

2.使用gevent + sleep自動將CPU執行權分配給當前未睡眠的協程

'''
使用gevent + sleep自動將CPU執行權分配給當前未睡眠的協程
'''
import gevent

def func1():
    gevent.sleep(1)
    print("大夢誰先覺")

    gevent.sleep(13)
    print("1:over")
    pass

def func2():
    gevent.sleep(3)
    print("生平我自知")

    gevent.sleep(9)
    print("2:over")
    pass

def func3():
    gevent.sleep(5)
    print("草堂春睡足")

    gevent.sleep(5)
    print("3:over")
    pass

def func4():
    gevent.sleep(7)
    print("窗外日遲遲")

    gevent.sleep(1)
    print("4:over")
    
def simpleGevent():
    gr1 = gevent.spawn(func1)
    gr2 = gevent.spawn(func2)
    gr3 = gevent.spawn(func3)
    gr4 = gevent.spawn(func4)
    gevent.joinall([
        gr1, gr2, gr3, gr4
    ])

if __name__ == '__main__':
    # simpleGevent()
    pass

3.經過monkey調度

'''
使用gevent + monkey.patch_all()自動調度網絡IO協程
'''
import gevent
import requests
import time
from gevent import monkey

def getPageText(url, order=0):
    print("No%d:%s請求開始..." % (order, url))
    resp = requests.get(url)  # 發起網絡請求,返回須要時間——阻塞IO

    html = resp.text
    print("No%d:%s成功返回:長度爲%d" % (order, url, len(html)))

# 將【標準庫-阻塞IO實現】替換爲【gevent-非阻塞IO實現】
monkey.patch_all()
if __name__ == '__main__':
    start = time.time()
    time.clock()
    gevent.joinall([
        gevent.spawn(getPageText, "http://www.sina.com", order=1),
        gevent.spawn(getPageText, "http://www.qq.com", order=2),
        gevent.spawn(getPageText, "http://www.baidu.com", order=3),
        gevent.spawn(getPageText, "http://www.163.com", order=4),
        gevent.spawn(getPageText, "http://www.4399.com", order=5),
        gevent.spawn(getPageText, "http://www.sohu.com", order=6),
        gevent.spawn(getPageText, "http://www.youku.com", order=7),
        gevent.spawn(getPageText, "http://www.iqiyi.com", order=8),
    ])

    end = time.time()
    print("over,耗時%d秒" % (end - start))
    print(time.clock())
    pass
相關文章
相關標籤/搜索