Python—進程、線程、協程

1、線程python


  線程是操做系統可以進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運做單位。一條線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程並行執行不一樣的任務python3.x

方法:bash

  start            線程準備就緒,等待CPU調度多線程

  setName      設置線程名稱併發

  getName      獲取線程名稱app

  setDaemon   把一個主進程設置爲Daemon線程後,主線程執行過程當中,後臺線程也在進行,主線程執行完畢後,後臺線程不論有沒執行完成,都會中止異步

  join              逐個執行每一個線程,執行完畢後繼續往下執行,該方法使得多線程變得無心義  async

  run              線程被cpu調度後自動執行線程對象的run方法ide



threading模塊函數

  線程的兩種調用方式:

1.直接調用(經常使用)

import threading
import time
'''直接調用'''

def hello(name):
    print("Hello %s"%name)
    time.sleep(3)
    
    
if __name__ == "__main__":
    t1=threading.Thread(target=hello,args=("zhangsan",)) #生成線程實例
    t2=threading.Thread(target=hello,args=("lisi",))
    t1.setName("aaa")   #設置線程名
    t1.start()  #啓動線程
    t2.start()
    t2.join()   #join  等待t2先執行完
    print("Hello")
    print(t1.getName()) #獲取線程名

2.繼承式調用


'''繼承式調用'''
import threading
import time

class MyThread(threading.Thread):
    def __init__(self,name):
        threading.Thread.__init__(self)
        self.name = name
        
    def run(self):
        print("Hello %s"%self.name)
        time.sleep(3)
        
if __name__ == "__main__":
    t1=MyThread("zhangsan")
    t2=MyThread("lisi")
    t1.start()
    t2.start()


setDaemon線程


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

def run(n):
    print('Hello..[%s]\n' % n)
    time.sleep(2)
    
def main():
    for i in range(5):
            t = threading.Thread(target=run,args=[i,])
      t.start()
      t.join(1)
        
m = threading.Thread(target=main,args=[])
m.setDaemon(True) #將主線程設置Daemon設置爲True後,主線程執行完成時,其它子線程會同時退出,不論是否執行完任務
m.start()
print("--- done----")

 

線程鎖Lock

  一個進程下能夠啓動多個線程,多個線程共享父進程的內存空間,每一個線程能夠訪問同一份數據,因此當多個線程同時要修改同一份數據時,就會出現錯誤

  例如:

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

num = 100 #設置一個共享變量

def show():
    global num  #在函數內操做函數外變量,需設置爲全局變量
    time.sleep(1)
    num -= 1
    
list=[]

for i in range(100):
    t = threading.Thread(target=show)
    t.start()
    list.append(t)
    
for t in list:
    t.join()
    
print(num)

  上面的例子在正常執行完成後的num的結果應該是0,但實際上每次的執行結果都不太同樣,由於當多個線程同時要修改同一份數據時,就會出現一些錯誤(只有

在python2.x運行纔會出現錯誤,python3.x中不會),因此每一個線程在要修改公共數據時,爲了不本身在還沒改完的時候別人也來修改此數據,能夠加上線程鎖

來確保每次修改數據時只有一個線程在操做。



加鎖代碼


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

num = 100 #設置一個共享變量
lock=threading.Lock()  #生成全局鎖

def show():
    global num  #在函數內操做函數外變量,需設置爲全局變量
    time.sleep(1)
    lock.acquire()  #修改前加鎖
    num -= 1
    lock.release()  #修改後解鎖
    
list=[]

for i in range(100):
    t = threading.Thread(target=show)
    t.start()
    list.append(t)
for t in list:
    t.join()
print(num)


遞歸鎖RLock

    就是在一個大鎖中再包含子鎖


#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
#遞歸鎖

def run1():
    lock.acquire()  #小鎖
    global num
    num +=1
    lock.release()
    return num
    
def run2():
    lock.acquire()  #小鎖
    global  num2
    num2+=1
    lock.release()
    return num2
    
def run3():
    lock.acquire()  #大鎖
    res = run1()
    res2 = run2()
    lock.release()
    print(res,res2)
    
if __name__ == '__main__':
    num,num2 = 0,0
    lock = threading.RLock()    #生成Rlock
    
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()
        
while threading.active_count() != 1:#若是不等於1,說明子線程還沒執行完畢
    pass #打印進程數
else:
    print(num,num2)


Semaphore

  同時容許必定數量的線程更改數據


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

def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s" %n)
    semaphore.release()
    
if __name__ == '__main__':
    semaphore  = threading.BoundedSemaphore(3) #設置最多容許3個線程同時運行
    for i in range(20):
        t = threading.Thread(target=run,args=(i,))
        t.start()
        
while threading.active_count() != 1:
    pass
else:
    print('----done---')


event

  實現兩個或多個線程間的交互,提供了三個方法 set、wait、clear,默認碰到event.wait 方法時就會阻塞。

  event.set(),設定後遇到wait不阻塞

  event.clear(),設定後遇到wait後阻塞

  event.isSet(),判斷有沒有被設定


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

def start():
    print("---start---1")
    event.wait()    #阻塞
    print("---start---2")
    
if __name__ == "__main__":
    event = threading.Event()
    t = threading.Thread(target=start)
    t.start()
    result=input(">>:")
    if result == "set":
        event.set() #設定set,wait不阻塞


2、進程

multiprocessing模塊

進程調用


#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process
import time

def start(name):
    time.sleep(1)
    print('hello', name)
    
if __name__ == '__main__':
    p = Process(target=start, args=('zhangsan',))
    p1 = Process(target=start, args=('lisi',))
    p.start()
    p1.start()
    p.join()


進程間通信

  每一個進程都擁有本身的內存空間,所以不一樣進程間內存是不共享的,要想實現兩個進程間的數據交換,有幾種方法

Queue(隊列)


#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Queue

def start(q):
    q.put( 'hello')
if __name__ == '__main__':
    q = Queue()
    p = Process(target=start, args=(q,))
    p.start()
    print(q.get())
    p.join()


Pipe(管道,不經常使用)

  把管道的兩頭分別賦給兩個進程,實現兩個進程的互相通訊


#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Pipe

def start(conn):
    conn.send('hello')#發送
    print(conn.recv())#接收
    conn.close()
    
if __name__ == '__main__':
    parent_conn, child_conn = Pipe()    #生成一個管道
    p = Process(target=start, args=(child_conn,))
    p.start()
    print(parent_conn.recv())#接收
    parent_conn.send("11111")#發送
    p.join()


Manager(實現了進程間真正的數據共享)


#!/usr/bin/env python
from multiprocessing import Process, Manager

def f(dic, list,i):
    dic['1'] = 1
    dic['2'] = 2
    dic['3'] = 3
    list.append(i)
    
if __name__ == '__main__':
    manager = Manager()
    dic = manager.dict()#經過manager生成一個字典
    list = manager.list(range(5))#經過manager生成一個列表
    p_list = []
    for i in range(10):
        p = Process(target=f, args=(dic, list,i))
        p.start()
        p_list.append(p)
    for res in p_list:
        res.join()
    print(dic)
    print(list)
#執行結果
'''
{'2': 2, '3': 3, '1': 1}
[0, 1, 2, 3, 4, 1, 9, 2, 5, 3, 7, 6, 0, 8, 4]
'''


進程池

  進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,若是進程池序列中沒有可供使用的進程,那麼程序就會等待,直到進程池中有可用進程爲止。

進程池中有兩個方法:

一、apply(同步)

二、apply_async(異步)


#!/usr/bin/env python
# -*- coding:utf-8 -*-
from  multiprocessing import Process,Pool
import time

def Foo(i):
    time.sleep(1)
    return i+100
def Bar(arg):
    print('number::',arg)
    
if __name__ == "__main__":
    pool = Pool(3)#定義一個進程池,裏面有3個進程
    for i in range(10):
        pool.apply_async(func=Foo, args=(i,),callback=Bar)
        #pool.apply(func=Foo, args=(i,))
    pool.close()#關閉進程池
    pool.join()#進程池中進程執行完畢後再關閉,(必須先close在join)


callback是回調函數,就是在執行完Foo方法後會自動執行Bar函數,而且自動把Foo函數的返回值做爲參數傳入Bar函數

 

3、協程

  協程,又稱微線程,是一種用戶態的輕量級線程。協程能保留上一次調用時的狀態,每次過程重入時,就至關於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置,當程序中存在大量不須要CPU的操做時(IO),適用於協程。

協程有極高的執行效率,由於子程序切換不是線程切換,而是由程序自身控制,所以,沒有線程切換的開銷。

不須要多線程的鎖機制,由於只有一個線程,也不存在同時寫變量衝突,在協程中控制共享資源不加鎖,只須要判斷狀態就行了,因此執行效率比多線程高不少。

由於協程是一個線程執行,因此想要利用多核CPU,最簡單的方法是多進程+協程,這樣既充分利用多核,又充分發揮協程的高效率。

那符合什麼條件就能稱之爲協程:一、必須在只有一個單線程裏實現併發 二、修改共享數據不需加鎖 三、用戶程序裏本身保存多個控制流的上下文棧 四、一個協程遇到IO操做自動切換到其它協程


python中對於協程有兩個模塊,greenlet和gevent。

Greenlet(greenlet的執行順序須要咱們手動控制)


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

def test1():
    print (11)
    gr2.switch()    #手動切換
    print (22)
    gr2.switch()
    
def test2():
    print (33)
    gr1.switch()
    print (44)
    
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()


gevent(自動切換,因爲切換是在IO操做時自動完成,因此gevent須要修改Python自帶的一些標準庫,這一過程在啓動時經過monkey patch完成)


from gevent import monkey; monkey.patch_all()
import gevent
import time

def foo():
    print('11')
    time.sleep(3)
    print('22')
    
def bar():
    print('33')
    print('44')
    
gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
])


運行結果:(從結果能夠看出,它們是併發執行的)

11
33
44
22
相關文章
相關標籤/搜索