1. 程式人生 > >python中的多工

python中的多工

 

多工

多工
    
    什麼是任務
        一個電腦執行這的軟體
    什麼是多工
        電腦同時執行著的多個軟體
    多工原理
        時間片的輪轉
    並行與併發
        併發:假的多工,多個任務共用一個核
        並行:正的多工,一個核處理一個程式
    生理過程(從生到死)
        建立 -> 就緒 -> 執行 -> 阻塞 -> 死亡
    執行緒和程序的建立一定要在主函式中,且主任務和子任務一起往下執行,遇到join()方法,主任務會等子任務執行完在結束
View Code

執行緒

執行緒
        特點
            檢視正在執行的執行緒列表
            threading.enumerate()
            只要程式已啟動,Python直譯器就會自動建立一個主執行緒
            主執行緒等待其他執行緒結束後在結束
            執行緒target指向的函式執行完畢,執行緒就結束了
            子執行緒是呼叫start()之後開啟的
            多個執行緒共享全域性變數 
            
                
        建立
            通過繼承的方式建立執行緒
                特點:寫法複雜,使用簡單,可以使用物件的特性(封裝、繼承、多型)
                       業務邏輯比較複雜時使用
            方式一
                
import threading p = threading.THread(target=函式名,args=(1,)) p.start() p.join() 方式二 import threading class MyThread(threading.Thread): def run(): 子執行緒要做的事情 t
= MyThread() t.start() t.join() 互斥鎖 -->科學家吃麵(筷子和碗) 1.什麼是資源競爭? 多個執行緒爭搶做同一件事 2.資源競爭會帶來什麼問題? 多執行緒共享同一個資源的時候,當操作這個資源(變數)足夠多的次數時,可能會出現問題 eg:1000000次的累加 3.如何解決資源競爭的問題? 互斥鎖 4.什麼是互斥鎖? 確保了某段關鍵程式碼只能由一個執行緒從頭到尾完整地執行 5.如何使用互斥鎖? 1.建立鎖物件 mutex = threading.Lock() 2.獲取鎖 mutex.acquire() 3.釋放鎖 mutex.release() 6.原則: 存在資源競爭的程式碼 加鎖的程式碼越少越少 7.死鎖 什麼是死鎖? 執行緒1等待執行緒2釋放鎖,執行緒2等待執行緒1釋放鎖 如何解決死鎖? 1.設計寫程式碼的時候就避免 2.設定超時時間 3.銀行家演算法
View Code

程序

程序
        1.程式:
            就是程式設計師寫的程式碼,沒有執行起來的程式碼
        2.程序
            執行起來的程式,程式碼+計算機資源
            程序是實現多工的第二種方式
        3.程式執行起來之後,Python直譯器會給程式建立一個程序,叫做主程序
        
        特點
            1.檢視程序
                windows: 工作管理員
                linux:ps -aux
                   殺死程序  kill -9 PID
                   top  htop 相當於windows中的工作管理員
            2.程序的寫實拷貝
                寫(修改)的時候拷貝一份
                程序不共享全域性變數
                通過args給程序傳遞資料
            3.獲取程序id和父程序的id
                os.getpid()
                os.getppid()
            4.主程序等待子程序結束後再結束(主程序替子程序收屍),程序的執行順便不確定
            
        建立
            方式一
                import multiprocessing
                p = multiprocessing.Process(target=函式名,args=(1,))
                p.start()
                p.join()
            方式二
                import multiprocessing
                class MyProcess(multiprocessing.Process):
                    def run():
                        print("子執行緒要做的事情")
                p = MyProcess()
                p.start()
                p.join()
        程序間通訊(傳遞資料)
            預設情況下,程序之間不能互相訪問資料
            佇列(Queue)
                常用的方法 
                    get()/put()/full()
                每個程序都可以往這個佇列中寫資料,都可以從這個佇列中讀取資料
            編碼步驟:
                建立佇列物件
                給佇列中放資料
                從佇列中取資料
        程序池(Pool)
            
            什麼是程序池?
                一次性在程序池中建立多個程序
            程序的作用?
                減少了銷燬執行緒的次數,從而提高效率
            如何使用程序池?
                建立程序池物件
                呼叫方法完成任務
        
            from multiprocessing import Pool
            import os, time, random
            def worker(msg):
                t_start = time.time()
                print("%s開始執行,程序號為%d" % (msg, os.getpid()))
                # random.random()隨機生成0~1之間的浮點數
                time.sleep(random.random() * 2)
                t_stop = time.time()
                print(msg, "執行完畢,耗時%0.2f" % (t_stop - t_start))


            def main():
                po = Pool(3)  # 定義一個程序池,最大程序數3
                for i in range(0, 10):
                    # Pool().apply_async(要呼叫的目標,(傳遞給目標的引數元祖,))
                    # 每次迴圈將會用空閒出來的子程序去呼叫目標
                    po.apply_async(worker, (i,))

                print("----start----")
                po.close()  # 關閉程序池,關閉後po不再接收新的請求
                time.sleep(1)
                # po.join()  # 等待po中所有子程序執行完成,必須放在close語句之後
                print("-----end-----")


            if __name__ == '__main__':
                main()
View Code

協程

協程
        什麼是可迭代物件
            一個普通的物件實現了iter內建函式
        什麼是迭代器
            一個普通的物件實現了iter和next內建函式
        迭代器的特點
            儲存的是生成資料的方式,而不直接儲存資料
            好處:節省系統空間
        什麼是生成器
            它是一個特殊的迭代器
        yield
            一個普通的函式裡面寫了yield的話,他就是一個生成器模板
            執行函式遇到yield會阻塞
            呼叫next()或者send()會解阻塞
            send()可以用來傳遞引數
            eg:
                def func(all_num):
                a, b = 0, 1
                count_num = 0
                while True:
                    if count_num < all_num:
                        result = yield a  # 如果一個函式中有yield,則說明這是特殊函式,叫生成器的模板
                        print(">>>>ret>>>>", result)
                        a, b = b, a + b
                        count_num += 1
                    else:
                        raise StopIteration

            f = func(10)
            ret = next(f)
            print(ret)
            ret = f.send("hahaha")   # 將這個結果傳遞給 result = yield a 讓result來儲存"hahaha"
            ret1 = next(f)
            print(ret1)      # 結果為None傳遞一次send後,後面的資料都需要send來傳輸,否則結果為None
            ret2 = f.send("ok")
            
        利用yield做協程
            寫多個函式,每個函式中都寫yield,函式執行時遇到yield就會阻塞
            然後交替著呼叫不同任務的next()方法,這樣就用協程實現了多工        
            原理:
                利用執行緒的空閒時間去執行其他的任務
            特點:
                協程依賴於執行緒,執行緒依賴程序
                從系統開銷講,程序>執行緒>協程
                
        建立協程
            yield  next()  send()
            
            import gevent
            import time
            from gevent import monkey

            monkey.patch_all()

            def func1(num):
                for i in range(num):
                    print(gevent.getcurrent(), i)
                    time.sleep(0.5)

            def func2(num):
                for i in range(num):
                    print(gevent.getcurrent(), i)
                    time.sleep(0.5)

            def func3(num):
                for i in range(num):
                    print(gevent.getcurrent(), i)
                    time.sleep(0.5)

            def func4(num):
                for i in range(num):
                    print(gevent.getcurrent(), i)
                    time.sleep(0.5)

            def func5(num):
                for i in range(num):
                    print(gevent.getcurrent(), i)
                    time.sleep(0.5)
            def main():
                gevent.joinall([gevent.spawn(func1, 10),
                                gevent.spawn(func2, 10),
                                gevent.spawn(func3, 10),
                                gevent.spawn(func4, 10),
                                gevent.spawn(func5, 10)
                                ])
                                
            if __name__ == "__main__":
                main()
View Code