欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python 高級教程之線程進程和協(xié)程的代碼解析

 更新時間:2022年05月05日 12:08:13   作者:海擁?  
這篇文章主要介紹了Python 高級教程之線程進程和協(xié)程的代碼解析,包括使用線程模塊的簡單示例,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

進程

進程是指在系統(tǒng)中正在運行的一個應(yīng)用程序,是 CPU 的最小工作單元。

進程 5 種基本狀態(tài)

一個進程至少具有 5 種基本狀態(tài):初始態(tài)、就緒狀態(tài)、等待(阻塞)狀態(tài)、執(zhí)行狀態(tài)、終止狀態(tài)。

  • 初始狀態(tài):進程剛被創(chuàng)建,由于其他進程正占有CPU資源,所以得不到執(zhí)行,只能處于初始狀態(tài)。
  • 就緒狀態(tài):只有處于就緒狀態(tài)的經(jīng)過調(diào)度才能到執(zhí)行狀態(tài)
  • 等待狀態(tài):進程等待某件事件完成
  • 執(zhí)行狀態(tài):任意時刻處于執(zhí)行狀態(tài)的進程只能有一個(對于單核CPU來講)。
  • 停止狀態(tài):進程結(jié)束

進程的特點

  • 動態(tài)性:進程是程序的一次執(zhí)行過程,動態(tài)產(chǎn)生,動態(tài)消亡。
  • 獨立性:進程是一個能獨立運行的基本單元。是系統(tǒng)分配資源與調(diào)度的基本單元。
  • 并發(fā)性:任何進程都可以與其他進程并發(fā)執(zhí)行。
  • 結(jié)構(gòu)性:進程由程序、數(shù)據(jù)和進程控制塊三部分組成。

multiprocessing 是比 fork 更高級的庫,使用 multiprocessing 可以更加輕松的實現(xiàn)多進程程序。

#!/usr/bin/env python
# -*- coding:utf-8 -*- 
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()

注意:由于進程之間的數(shù)據(jù)需要各自持有一份,所以創(chuàng)建進程需要的非常大的開銷。并且python不能再Windows下創(chuàng)建進程!

使用多進程的時候,最好是創(chuàng)建和和 CPU 核數(shù)相等的進程數(shù)。

進程間數(shù)據(jù)共享

系統(tǒng)中的進程與其他進程共享 CPU 和主存資源,為了更好的管理主存,操作系統(tǒng)提供了一種對主存的抽象概念,即為虛擬存儲器(VM)。它也是一個抽象的概念,它為每一個進程提供了一個假象,即每個進程都在獨占地使用主存。

虛擬存儲器主要提供了三個能力:

  • 將主存看成是一個存儲在磁盤上的高速緩存,在主存中只保存活動區(qū)域,并根據(jù)需要在磁盤和主存之間來回傳送數(shù)據(jù),通過這種方式,更高效地使用主存
  • 為每個進程提供一致的地址空間,從而簡化存儲器管理
  • 保護每個進程的地址空間不被其他進程破壞

由于進程擁有自己獨占的虛擬地址空間,CPU通過地址翻譯將虛擬地址轉(zhuǎn)換成真實的物理地址,每個進程只能訪問自己的地址空間。因此,在沒有其他機制(進程間通信)的輔助下,進程之間是無法共享數(shù)據(jù)的。

進程各自持有一份數(shù)據(jù),默認無法共享數(shù)據(jù)。默認的進程之間相互是獨立,如果想讓進程之間數(shù)據(jù)共享,就得有個特殊的數(shù)據(jù)結(jié)構(gòu),這個數(shù)據(jù)結(jié)構(gòu)就可以理解為他有穿墻的功能 如果你能穿墻的話兩邊就都可以使用了

#!/usr/bin/env python
#coding:utf-8
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

使用特殊的數(shù)據(jù)類型,來進行穿墻:

#通過特殊的數(shù)據(jù)結(jié)構(gòu):數(shù)組(Array)
from multiprocessing import Process,Array
#創(chuàng)建一個只包含數(shù)字類型的數(shù)組(python中叫列表)
#并且數(shù)組是不可變的,在C,或其他語言中,數(shù)組是不可變的,之后再python中數(shù)組(列表)是可以變得
#當然其他語言中也提供可變的數(shù)組
#在C語言中數(shù)組和字符串是一樣的,如果定義一個列表,如果可以增加,那么我需要在你內(nèi)存地址后面再開辟一塊空間,那我給你預留多少呢?
#在python中的list可能用鏈表來做的,我記錄了你前面和后面是誰。列表不是連續(xù)的,數(shù)組是連續(xù)的
'''
上面不是列表是“數(shù)組"數(shù)組是不可變的,附加內(nèi)容是為了更好的理解數(shù)組!
'''
temp = Array('i', [11,22,33,44]) #這里的i是C語言中的數(shù)據(jù)結(jié)構(gòu),通過他來定義你要共享的內(nèi)容的類型!點進去看~
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()共享數(shù)據(jù)
from multiprocessing import Process,Manager  #這個特殊的數(shù)據(jù)類型Manager
manage = Manager()
dic = manage.dict() #這里調(diào)用的時候,使用字典,這個字典和咱們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()

既然進程之間可以進行共享數(shù)據(jù),如果多個進程同時修改這個數(shù)據(jù)是不是就會造成臟數(shù)據(jù)?是不是就得需要鎖!

進程的鎖和線程的鎖使用方式是非常一樣的知識他們是用的類是在不同地方的。

進程池

進程池內(nèi)部維護一個進程序列,當使用時,則去進程池中獲取一個進程,如果進程池序列中沒有可供使用的進進程,那么程序就會等待,直到進程池中有可用進程為止。

進程池中有兩個方法:

  • apply
  • apply_async
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from  multiprocessing import Process,Pool
import time
def Foo(i):
    time.sleep(2)
    return i+100
def Bar(arg):
    print arg
pool = Pool(5) #創(chuàng)建一個進程池
#print pool.apply(Foo,(1,))#去進程池里去申請一個進程去執(zhí)行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()#進程池中進程執(zhí)行完畢后再關(guān)閉,如果注釋,那么程序直接關(guān)閉。
'''
apply 主動的去執(zhí)行
pool.apply_async(func=Foo, args=(i,),callback=Bar) 相當于異步,當申請一個線程之后,執(zhí)行FOO方法就不管了,執(zhí)行完之后就在執(zhí)行callback ,當你執(zhí)行完之后,在執(zhí)行一個方法告訴我執(zhí)行完了
callback 有個函數(shù),這個函數(shù)就是操作的Foo函數(shù)的返回值!
'''

進程的缺點

無法即時完成的任務(wù)帶來大量的上下文切換代價與時間代價。

進程的上下文:當一個進程在執(zhí)行時,CPU的所有寄存器中的值、進程的狀態(tài)以及堆棧中的內(nèi)容被稱為該進程的上下文。

上下文切換:當內(nèi)核需要切換到另一個進程時,它需要保存當前進程的所有狀態(tài),即保存當前進程的上下文,以便在再次執(zhí)行該進程時,能夠得到切換時的狀態(tài)并執(zhí)行下去。

線程

線程的定義

在計算中,進程是正在執(zhí)行的計算機程序的一個實例。任何進程都有 3 個基本組成部分:

  • 一個可執(zhí)行程序。
  • 程序所需的相關(guān)數(shù)據(jù)(變量、工作空間、緩沖區(qū)等)
  • 程序的執(zhí)行上下文(進程狀態(tài))

線程是進程中可以調(diào)度執(zhí)行的實體。此外,它是可以在 OS(操作系統(tǒng))中執(zhí)行的最小處理單元。

簡而言之,線程是程序中的一系列此類指令,可以獨立于其他代碼執(zhí)行。為簡單起見,您可以假設(shè)線程只是進程的子集!

線程在線程控制塊 (TCB)中包含所有這些信息:

  • 線程標識符:為每個新線程分配唯一 id (TID)
  • 堆棧指針:指向進程中線程的堆棧。堆棧包含線程范圍內(nèi)的局部變量。
  • 程序計數(shù)器:存放線程當前正在執(zhí)行的指令地址的寄存器。
  • 線程狀態(tài):可以是running、ready、waiting、start或done。
  • 線程的寄存器集:分配給線程進行計算的寄存器。
  • 父進程指針:指向線程所在進程的進程控制塊 (PCB) 的指針。

多線程被定義為處理器同時執(zhí)行多個線程的能力。

在一個簡單的單核 CPU 中,它是通過線程之間的頻繁切換來實現(xiàn)的。這稱為上下文切換。在上下文切換中,只要發(fā)生任何中斷(由于 I/O
或手動設(shè)置),就會保存一個線程的狀態(tài)并加載另一個線程的狀態(tài)。上下文切換發(fā)生得如此頻繁,以至于所有線程似乎都在并行運行(這被稱為多任務(wù))。

在 Python 中,threading模塊提供了一個非常簡單直觀的 API,用于在程序中生成多個線程。

使用線程模塊的簡單示例

讓我們考慮一個使用線程模塊的簡單示例:

# Python程序說明線程的概念
# 導入線程模塊
import threading
def print_cube(num):
    """
    打印給定數(shù)字立方的函數(shù)
    """
    print("立方: {}".format(num * num * num))
def print_square(num):
    """
    打印給定數(shù)字平方的函數(shù)
    """
    print("平方: {}".format(num * num))
if __name__ == "__main__":
    # creating thread
    t1 = threading.Thread(target=print_square, args=(10,))
    t2 = threading.Thread(target=print_cube, args=(10,))
    # starting thread 1
    t1.start()
    # starting thread 2
    t2.start()
    # 等到線程 1 完全執(zhí)行
    t1.join()
    # 等到線程 2 完全執(zhí)行
    t2.join()
    # 兩個線程完全執(zhí)行
    print("完成!")

平方: 100
立方: 1000
完成!

代碼解析

讓我們試著理解上面的代碼:

  • 要導入線程模塊,我們這樣做:
import threading
  • 要創(chuàng)建一個新線程,我們創(chuàng)建一個Thread類的對象。它需要以下參數(shù):
  • target : 線程要執(zhí)行的函數(shù)
  • args:要傳遞給目標函數(shù)的參數(shù)

在上面的示例中,我們創(chuàng)建了 2 個具有不同目標函數(shù)的線程:

t1 = threading.Thread(target=print_square, args=(10,)) 
t2 = threading.Thread(target=print_cube, args=(10,))

要啟動一個線程,我們使用 Thread 類的 start 方法。

t1.start() 
t2.start()

一旦線程啟動,當前程序(你可以把它想象成一個主線程)也會繼續(xù)執(zhí)行。為了在線程完成之前停止當前程序的執(zhí)行,我們使用join方法。

t1.join() 
t2.join()

結(jié)果,當前程序?qū)⑹紫鹊却?t1 的完成,然后 t2 。一旦它們完成,則執(zhí)行當前程序的剩余語句。

協(xié)程

協(xié)程(Coroutine,又稱微線程,纖程)是一種比線程更加輕量級的存在,協(xié)程不是被操作系統(tǒng)內(nèi)核所管理,而完全是由程序所控制。

我們都熟悉函數(shù),也稱為子例程、過程、子過程等。函數(shù)是打包為一個單元以執(zhí)行特定任務(wù)的指令序列。當一個復雜函數(shù)的邏輯被分成幾個獨立的步驟,這些步驟本身就是函數(shù)時,這些函數(shù)被稱為輔助函數(shù)或子程序。

Python 中的子程序由負責協(xié)調(diào)這些子程序的使用的主函數(shù)調(diào)用。子程序只有一個入口點。 協(xié)程是子程序的泛化。它們用于協(xié)作式多任務(wù)處理,其中一個進程定期或在空閑時自愿放棄(放棄)控制權(quán),以使多個應(yīng)用程序能夠同時運行。協(xié)程和子程序的區(qū)別是:

  • 與子程序不同,協(xié)程有許多用于暫停和恢復執(zhí)行的入口點。協(xié)程可以暫停其執(zhí)行并將控制權(quán)轉(zhuǎn)移給其他協(xié)程,并且可以從中斷點重新開始執(zhí)行。
  • 與子程序不同,沒有主函數(shù)可以按特定順序調(diào)用協(xié)程并協(xié)調(diào)結(jié)果。協(xié)程是協(xié)作的,這意味著它們鏈接在一起形成管道。一個協(xié)程可能會使用輸入數(shù)據(jù)并將其發(fā)送給其他處理它的協(xié)程。最后,可能會有一個協(xié)程來顯示結(jié)果。

協(xié)程與線程

現(xiàn)在您可能在想?yún)f(xié)程與線程有何不同,兩者似乎都在做同樣的工作。
在線程的情況下,它是根據(jù)調(diào)度程序在線程之間切換的操作系統(tǒng)(或運行時環(huán)境)。而在協(xié)程的情況下,決定何時切換協(xié)程的是程序員和編程語言。協(xié)程通過程序員在設(shè)定點暫停和恢復來協(xié)同工作多任務(wù)。

Python 協(xié)程

在 Python 中,協(xié)程類似于生成器,但幾乎沒有額外的方法,而且我們使用yield語句的方式也有細微的變化。生成器為迭代生成數(shù)據(jù),而協(xié)程也可以使用數(shù)據(jù)。
在 Python 2.5 中,引入了對 yield 語句的輕微修改,現(xiàn)在 yield 也可以用作表達式。例如在作業(yè)的右側(cè)——

line = (yield)

我們發(fā)送給協(xié)程的任何值都會被(yield)表達式捕獲并返回。

可以通過send()方法將值發(fā)送到協(xié)程。例如,考慮這個協(xié)程,它打印出帶有前綴“Dear”的名稱。我們將使用 send() 方法將名稱發(fā)送到協(xié)程。

# 用于演示協(xié)程執(zhí)行的 Python3 程序
def print_name(prefix):
    print("Searching prefix:{}".format(prefix))
    while True:
        name = (yield)
        if prefix in name:
            print(name)
# 調(diào)用協(xié)程,什么都不會發(fā)生
corou = print_name("Dear")
# 這將開始執(zhí)行協(xié)程并打印第一行 "Searching prefix..."
# 并將執(zhí)行推進到第一個 yield 表達式
corou.__next__()
# 發(fā)送輸入
corou.send("Haiyong")
corou.send("Dear Haiyong")

輸出:

Searching prefix:Dear
Dear Haiyong

協(xié)程的執(zhí)行

協(xié)程的執(zhí)行類似于生成器。當我們調(diào)用協(xié)程時,什么都沒有發(fā)生,它只在響應(yīng)next()send ()方法時運行。在上面的例子中可以清楚地看到這一點,因為只有在調(diào)用__next__()方法之后,我們的協(xié)程才開始執(zhí)行。在這個調(diào)用之后,執(zhí)行前進到第一個 yield 表達式,現(xiàn)在執(zhí)行暫停并等待值被發(fā)送到 corou 對象。當?shù)谝粋€值被發(fā)送給它時,它會檢查前綴和打印名稱(如果存在前綴)。打印完名稱后,它會遍歷循環(huán),直到再次遇到name = (yield)表達式。

關(guān)閉協(xié)程

協(xié)程可能無限期運行,關(guān)閉協(xié)程使用close()方法。當協(xié)程關(guān)閉時,它會生成GeneratorExit異常,該異??梢砸酝ǔ2东@的方式捕獲。關(guān)閉協(xié)程后,如果我們嘗試發(fā)送值,它將引發(fā)StopIteration異常。下面是一個簡單的例子:

# Python3 program for demonstrating
# closing a coroutine
def print_name(prefix):
    print("Searching prefix:{}".format(prefix))
    try :
        while True:
                name = (yield)
                if prefix in name:
                    print(name)
    except GeneratorExit:
            print("關(guān)閉協(xié)程!!")
corou = print_name("Dear")
corou.__next__()
corou.send("Haiyong")
corou.send("Dear Haiyong")
corou.close()

輸出:

搜索前綴:Dear 
Dear Haiyong
關(guān)閉協(xié)程!!

鏈接協(xié)程以創(chuàng)建管道

協(xié)程可用于設(shè)置管道。我們可以使用 send() 方法將協(xié)程鏈接在一起并通過管道推送數(shù)據(jù)。管道需要:

  • 初始源(生產(chǎn)者)派生整個管道。生產(chǎn)者通常不是協(xié)程,它只是一個簡單的方法。
  • 一個 sink,它是管道的端點。接收器可能會收集所有數(shù)據(jù)并顯示它。

以下是一個簡單的鏈接示例

# 用于演示協(xié)程鏈接的 Python 程序

def producer(sentence, next_coroutine):
    '''
    producer 只是拆分字符串并將其
    提供給 pattern_filter 協(xié)程
    tokens = sentence.split(" ")
    for token in tokens:
        next_coroutine.send(token)
    next_coroutine.close()
def pattern_filter(pattern="ing", next_coroutine=None):
    在接收到的令牌中搜索模式,如果模式匹配,
    將其發(fā)送到 print_token() 協(xié)程進行打印
    print("Searching for {}".format(pattern))
    try:
        while True:
            token = (yield)
            if pattern in token:
                next_coroutine.send(token)
    except GeneratorExit:
        print("過濾完成!!")
def print_token():
    充當接收器,只需打印接收到的令牌
    print("我沉了,我會打印令牌")
            print(token)
        print("打印完成!")
pt = print_token()
pt.__next__()
pf = pattern_filter(next_coroutine = pt)
pf.__next__()
sentence = "Haiyong is running behind a fast moving car"
producer(sentence, pf)

輸出:

我沉了,我會打印令牌
Searching for ing
running 
moving 
過濾完成!
打印完成!

總結(jié)

1.線程和協(xié)程推薦在 IO 密集型的任務(wù)(比如網(wǎng)絡(luò)調(diào)用)中使用,而在CPU密集型的任務(wù)中,表現(xiàn)較差。
2.對于CPU密集型的任務(wù),則需要多個進程,繞開GIL的限制,利用所有可用的CPU核心,提高效率。
3.在高并發(fā)下的最佳實踐就是多進程+協(xié)程,既充分利用多核,又充分發(fā)揮協(xié)程的高效率,可獲得極高的性能。

  • CPU 密集型: 多進程
  • IO 密集型: 多線程(協(xié)程維護成本較高,而且在讀寫文件方面效率沒有顯著提升)
  • CPU 密集和 IO 密集: 多進程+協(xié)程

到此這篇關(guān)于Python 高級教程之線程進程和協(xié)程的代碼解析的文章就介紹到這了,更多相關(guān)Python線程進程和協(xié)程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python中的ConfigParser模塊使用詳解

    Python中的ConfigParser模塊使用詳解

    這篇文章主要介紹了Python中的ConfigParser模塊的使用,ConfigParser模塊主要被用來讀寫配置模塊,需要的朋友可以參考下
    2015-05-05
  • PyQt 線程類 QThread使用詳解

    PyQt 線程類 QThread使用詳解

    QThread是我們將要詳細介紹的第一個類。它也是 Qt 線程類中最核心的底層類。由于 PyQt 的跨平臺特性,QThread要隱藏掉所有平臺相關(guān)的代碼。
    2017-07-07
  • python歸并排序算法過程實例講解

    python歸并排序算法過程實例講解

    在本篇文章里小編給大家整理的是一篇關(guān)于python歸并排序算法過程實例講解內(nèi)容,有興趣的朋友們可以學習下。
    2020-11-11
  • Python?4種實現(xiàn)定時任務(wù)的方案

    Python?4種實現(xiàn)定時任務(wù)的方案

    這篇文章主要給大家分享了Python?4種實現(xiàn)定時任務(wù)的方案,運用 while True: + sleep()、Timeloop 庫、threading.Timer 、內(nèi)置模塊 sched ,下面就來看看具體的實現(xiàn)過程吧
    2021-12-12
  • 樹莓派極簡安裝OpenCv的方法步驟

    樹莓派極簡安裝OpenCv的方法步驟

    這篇文章主要介紹了樹莓派極簡安裝OpenCv的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-10-10
  • Django實現(xiàn)前臺上傳并顯示圖片功能

    Django實現(xiàn)前臺上傳并顯示圖片功能

    這篇文章主要介紹了Django實現(xiàn)前臺上傳并顯示圖片功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05
  • 將.py文件轉(zhuǎn)化為.exe文件的詳細過程

    將.py文件轉(zhuǎn)化為.exe文件的詳細過程

    學Python那么久了,才知道自己不會把腳本編譯成可執(zhí)行exe文件,下面這篇文章主要給大家介紹了關(guān)于將.py文件轉(zhuǎn)化為.exe文件的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-09-09
  • TensorFlow變量管理詳解

    TensorFlow變量管理詳解

    這篇文章主要為大家詳細介紹了TensorFlow變量管理的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 在Django的form中使用CSS進行設(shè)計的方法

    在Django的form中使用CSS進行設(shè)計的方法

    這篇文章主要介紹了在Django的form中使用CSS進行設(shè)計的方法,Django是Python重多人氣開發(fā)框架中最為著名的一個,需要的朋友可以參考下
    2015-07-07
  • Python實現(xiàn)求解斐波那契第n項的解法(包括矩陣乘法+快速冪)

    Python實現(xiàn)求解斐波那契第n項的解法(包括矩陣乘法+快速冪)

    這篇文章主要介紹怎么使用Python求解斐波那契第n項,方法多樣,邏輯清晰,代碼簡單詳細,有這方面需要的朋友可以參考下
    2021-04-04

最新評論