1. 程式人生 > >Python 多執行緒 多程序 協程 yield

Python 多執行緒 多程序 協程 yield

python中多執行緒和多程序的最大區別是穩定性和效率問題

多程序互相之間不影響,一個崩潰了不影響其他程序,穩定性高 
多執行緒因為都在同一程序裡,一個執行緒崩潰了整個程序都完蛋

多程序對系統資源開銷大,多執行緒對系統資源開銷小,所以這方面來說多執行緒會比多程序快一點點

關於執行緒和程序的詳細使用方法這裡有https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319272686365ec7ceaeca33428c914edf8f70cca383000

等看完協程再補上

i/o密集型任務瓶頸都在網路,磁碟這些地方,適合用指令碼語言進行多工操作

3.協程
子程式呼叫總是一個入口,一次返回,呼叫順序是明確的。而協程的呼叫和子程式不同。

協程看上去也是子程式,但執行過程中,在子程式內部可中斷,然後轉而執行別的子程式,在適當的時候再返回來接著執行。 
協 
程最大的優勢是高執行效率,因為子程式切換不是執行緒切換,而是由程式自己控制,沒有切換執行緒的開銷 
不需要多執行緒鎖

協程是建立在生成器基礎上的,所以需要先理解迭代器生成器!!! 
在一個執行緒裡面不等待IO操作返回的結果,通過訊息輪詢來發送一個IO操作的請求後就執行其他的,等IO操作返回結果訊息了再處理.這個不死等的方式不需要多執行緒多程序,在一個執行緒裡實現,大大的提高了執行效率

對序列,字典,檔案都可以使用iter()方法生成可迭代物件,然後用next()方法訪問,這種東西咱們叫迭代器,一般用for迴圈讀出內容

生成器是可迭代的

簡單的生成器
my_generator = (x*x for x in range(4))
1
dir(my_generator ) 裡面有iter()和next()屬性,說明生成器是迭代器,可以用for迴圈讀取內部的值

>>> for i in my_generator:
...     print i
... 
0
1
4
9
>>> for i in my_generator:
...     print i
... 

當第一遍迴圈的時候,將 my_generator 裡面的值依次讀出並列印,但是,當再讀一次的時候,就發現沒有任何結果。

這種特性也正是迭代器所具有的,因為已經迭代到最後,指標指向沒有內容了

生成器my_generator = (x*x for x in range(4))和列表解析式my_list = [x*x for x in range(4)]有啥不一樣的地方呢,列表是先劃分出記憶體來放列表中的資料的,生成器是用到的時候再申請記憶體,針對大量值的時候,列表佔記憶體較多,迭代器(生成器是迭代器)的優勢就在於少佔記憶體,還有一點區別就是上面那個for迴圈讀取內部的值,列表是可以反覆取值的,不像生成器指標到最後沒有了就不走了,列表的記憶體劃分好了,每次想取值都可以

>>> for i in my_list:
...     print i
... 
0
1
4
9
>>> for i in my_list:
...     print i
... 
0
1
4
9

yield
python中有個詞yield ,這傢伙尼瑪是生成器的標誌 
比如

def g():
    yield 0
    yield 1
    yield 2

lalalal = g()
print type(lalalal)
print dir(lalalal)

#輸出
<type 'generator'>
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

有iter和next,說明是迭代器,用lalalal.next()可以取值的 
從上面的例子可以明白,含有yield關鍵字的函式返回值是一個生成器型別的物件,也是迭代器

我們任性的吧含有yield語句的函式叫做生成器,生成器是一種用普通函式語法定義的迭代器,反正記住,普通函式只要用了yield就變態成為生成器了,當然生成器具備迭代器屬性

我們來看看在函式裡 yield和return的具體區別

def r_return(n):
     print "You taked me."
     while n > 0:
         print "before return"

         return n
         n -= 1
         print "after return"
rr = r_return(3)
print rr

#輸出 
You taked me.
before return
3
#可以看出來,普通函式遇到return將值返回玩後,函式就不往下繼續執行了

#再看用yield將函式變為generator生成器之後的樣子
def r_return(n):
    print "You taked me."
    while n > 0:
        print "before return"

        yield n
        n -= 1
        print "after return"


rr = r_return(3)
print rr.next()
print rr.next()
print rr.next()


#輸出
You taked me.
before return
3
after return
before return
2
after return
before return
1
#可以看出,用了yield比return好啊,返回完值(迭代一次),還可以再接著執行後面的內容(迭代第二次,第三次),直到跳出迴圈(沒有可迭代的東西了)

一般的函式,都是止於 return。作為生成器的函式,由於有了 yield,則會遇到它掛起,如果還有 return,遇到它就直接丟擲 SoptIteration 異常而中止迭代。

send
上面的生成器都是固定的,肚子裡有啥就輸出什麼,自從python2.5之後,生成器有了一個新的特性,就是send方法,用它能直接往生成器的肚子裡面傳遞資料了,讓生成器不再是固定輸出了

使用send可以接受一個外部傳入的變數,然後根據變數內容計算結果之後返回,咱們之後要說的協程就是靠生成器函式的這個特性

def repeater():
    n=None
    while 1:
        n = (yield n)

r= repeater()
r.send(None)
print r.send("new friend come in")
print r.send("tseeee")


#輸出
new friend come in
tseeee

說下流程 
這裡和傳統函式呼叫不一樣,敲黑板,要通過r.send(None)(ps:如果用了send起手必須傳None)或者r.next()或者next(r)來啟動生成器函式,並進行到第一個yield語句結束的位置.此時,執行完了yield這一行語句,這個時候括號裡的yield n返回初始值0,返回值n=0

然後send(“new friend come in”)這裡會傳入new friend come in,從下一行開始繼續執行,並回到while頭部,執行yield n這一行返回n=new friend come in了之後又停下來了,

.再執行r.send(“tseeee”),重複上一步,執行到yield n這一行返回n=tseeee之後再一次停下來
--------------------- 
作者:Peace & Love 
來源:CSDN 
原文:https://blog.csdn.net/u013205877/article/details/77429518?utm_source=copy 
版權宣告:本文為博主原創文章,轉載請附上博文連結!