Day10 PythonWeb全棧課程課堂內容

小學玍發表於2021-01-03

1. 程式和程式

  • 區別:沒有執行的程式碼叫做程式,如pycharm.exe,而當程式碼執行起來以後就是程式。
  • 一個程式可以對應多個程式。

程式的排程

  • 先來先服務排程演算法:對長作業有利,對短作業無益。
  • 短作業優先排程演算法。
  • 時間片輪轉 + 多級反饋列隊。

程式狀態介紹

image.png

# 進入就緒狀態
import time

# 程式開始執行
print("start")

# 阻塞狀態
name = input("input:")
# 使用者輸入,解除阻塞狀態。
# 就緒

# 程式開始執行
print(name)

# 阻塞
time.sleep(1)
# 就緒

print('end')
# 結束

同步和非同步

  • 同步:任務提交之後,原地等待任務的返回結果,等待的過程中不做任何事情。
  • 非同步:任務提交之後,不原地等待任務的返回結果,直接做其他的事情。

阻塞和非阻塞

  • 阻塞:程式停滯,不會繼續往下走。
  • 非阻塞:程式不停滯,繼續往下走。 就緒態、執行態。

2. python 實現多程式

通過 multiprocessing.Process模組

  • group:引數未使用,預設值為None。
  • target:表示呼叫物件,即子程式要執行的任務。
  • args:表示呼叫的位置引數元祖。
  • kwargs:表示呼叫物件的字典。
  • name:子程式名稱
import multiprocessing
import time


def task1():
    while True:
        print('--1--')
        time.sleep(1)

def task2():
    while True:
        print('--1--')
        time.sleep(2)

def main():

    p1 = multiprocessing.Process(target=task1)
    p2 = multiprocessing.Process(target=task2)
    p1.start()
    p2.start()


if __name__ == '__main__':
    main()

在這裡插入圖片描述

注意:多個程式同時執行的順序是隨機的。

通過繼承Process類建立程式

import multiprocessing
import time

class MyProcess(multiprocessing.Process):

    def run(self):
        print('---')
        time.sleep(1)

c = MyProcess()
c.run()

程式與執行緒區別

  • 根本區別

    程式:作業系統資源分配的基本單位

    執行緒:任務排程和執行的基本單位

  • 開銷

    程式:通過複製程式碼+資源建立子程式 每個程式都有獨立的程式碼和資料空間,程式之間的切換會有較大的開銷

    執行緒:在同一份程式碼裡 建立執行緒 共享記憶體 開銷較小

  • 分配記憶體

    程式:系統在執行的時候為每個程式分配不同的記憶體空間

    執行緒:執行緒所使用的資源是它所屬的程式的資源

  • 包含關係

    程式:一個程式可以擁有多個執行緒

    執行緒:執行緒是程式的一部分

    image.png

    image.png

    image.png

3. 程式join方法

from multiprocessing import Process
import time


def task(n):
    print("hello", n)
    time.sleep(n)
    print('Python', n)


if __name__ == '__main__':
    p1 = Process(target=task, args=(1,))
    p2 = Process(target=task, args=(2,))
    p3 = Process(target=task, args=(3,))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    print(time.time() - start_time) # 主程式的時間

在這裡插入圖片描述

  • 如果我們要等到子程式結束之後列印時間。
from multiprocessing import Process
import time


def task(n):
    print("hello", n)
    time.sleep(n)
    print('Python', n)


if __name__ == '__main__':
    p1 = Process(target=task, args=(1,))
    p2 = Process(target=task, args=(2,))
    p3 = Process(target=task, args=(3,))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    print(time.time() - start_time)

在這裡插入圖片描述

加上join()之後會發現時間變為越3s,相當於本程式中等,p3.jion()子程式結束之後計算p3子程式的時間。

同樣的將print(time.time() - start_time)放置在p2.join()之後你會得到約2s的時間,表示在計算p2子程式的時間。放置在p1之後也是同一個道理。


from multiprocessing import Process
import time


def task(n):
    print("hello", n)
    time.sleep(n)
    print('Python', n)


if __name__ == '__main__':
    start_time = time.time()
    for i in range(1, 4):
        p = Process(target=task, args=(i, ))
        p.start()
        p.join()
    print(time.time() - start_time)

在這裡插入圖片描述

為什麼時間邊長?:因為前面的程式是p1.start(),p2.start(),p3.start()是同時執行的,而現在迴圈內部先p1.start()結束之後,再p2.start(),最後p3.start(),所以時間就比較長了。

  • 解決此問題
# 建立一個列表
from multiprocessing import Process
import time


def task(n):
    print("hello", n)
    time.sleep(n)
    print('Python', n)


if __name__ == '__main__':
    start_time = time.time()
    p_list = []
    for i in range(1, 4):
        p = Process(target=task, args=(i, ))
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print(time.time() - start_time)

在這裡插入圖片描述


4. 佇列

from multiprocessing import Queue

# 建立物件 佇列
q = Queue(3)


# 存資料
q.put(3)
q.put("hello")
q.put([1,2])

# 取資料
print(q.get())
print(q.get())
print(q.get())

注意:

存資料的型別並沒有限制。

Queue內的數值表示能儲存多少資料。當Queue(3)時只能儲存q.put(3),q.put("hello"),q.put([1,2])當儲存資料大於建立物件的佇列,就會使得程式發生停滯。

取資料裡面當取資料的數量大於儲存資料的數量時,同樣最後程式也會發生停滯。

full()方法,用於判斷佇列內是否為滿。

empty()方法,用於判斷佇列內是否為空。

put_nowait()方法,本質上和put()一樣,只不過如果佇列滿了就會報異常。

get_nowait()方法,本質上和get()一樣,只不過如果佇列空了就會報異常。

  • 佇列作用

image.png

image.png

程式1和程式2之間完全不限解耦,只要經過中間灰色的佇列,程式1將檔案放在佇列中,程式2從佇列中獲得。

5. 程式間全域性變數的共享

import multiprocessing

a = 100


def task():
    global a
    a += 100


def task1():
    print(a)

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=task)
    p2 = multiprocessing.Process(target=task1)

    p1.start()
    p2.start()

在這裡插入圖片描述

證明多程式之間的全域性變數是不受影響的,且為相互獨立的。

6. 佇列的簡單通訊

# @Time : 2021/1/3 2:10
# @Author : Sam
# @File : 佇列的簡單通訊.py
# @Software: PyCharm

import multiprocessing


def download(q):
    '''下載資料'''
    li = [1, 2, 3]
    for item in li:
        q.put(item)


def analysis(q):
    '''資料處理'''
    data = []
    while True:
        q_data = q.get()
        data.append(q_data)
        if q.empty():
            break
    print(data)


def main():
    # 建立一個佇列
    q = multiprocessing.Queue()
    p1 = multiprocessing.Process(target=download, args=(q,))
    p2 = multiprocessing.Process(target=analysis, args=(q,))
    p1.start()
    p2.start()


if __name__ == '__main__':
    main()

在這裡插入圖片描述

  • 模擬下載延時
import multiprocessing
import time
import random


def download(q):
    '''下載資料'''
    li = [1, 2, 3]
    for item in li:
        q.put(item)
        time.sleep(random.randint(1, 3))


def analysis(q):
    '''資料處理'''
    data = []
    while True:
        q_data = q.get()
        data.append(q_data)
        if q.empty():
            break
    print(data)


def main():
    # 建立一個佇列
    q = multiprocessing.Queue()
    p1 = multiprocessing.Process(target=download, args=(q,))
    p2 = multiprocessing.Process(target=analysis, args=(q,))
    p1.start()
    p1.join()
    p2.start()


if __name__ == '__main__':
    main()

start() 與 run() 區別

  • start() 方法來啟動程式,真正實現了多程式執行,這時無需等待 run 方法體程式碼執行完畢而直接繼續執行下面的程式碼:呼叫 Process 類的 start() 方法來啟動一個程式,這時此程式處於就緒(可執行)狀態,並沒有執行,一旦得到 cpu 時間片,就開始執行 run() 方法,這裡方法 run() 稱為程式體,當程式結束後,不可以重新啟動。
  • run() 方法只是類的一個普通方法,如果直接呼叫 run 方法,程式中依然只有主執行緒這一個執行緒,其程式執行路徑還是隻有一條,還是要順序執行,還是要等待 run 方法體執行完畢後才可繼續執行下面的程式碼,這樣就沒有達到寫執行緒的目的。
# 特殊情況 持續執行
import multiprocessing


def download(q):
    '''下載資料'''
    li = [1, 2, 3]
    for item in li:
        q.put(item)


def analysis(q):
    '''資料處理'''
    data = []
    while True:
        print(q.qsize())
        q_data = q.get()
        data.append(q_data)
        if q.empty():
            break
    print(data)


def main():
    # 建立一個佇列
    q = multiprocessing.Queue()
    download(q)
    analysis(q)


if __name__ == '__main__':
    main()

在這裡插入圖片描述

在這裡插入圖片描述

  • 為什麼會只出現兩個值原因是佇列中恰巧放進去值時,也恰好取出,造成了佇列中為空值,迴圈結束!

  • 解決方法:將佇列變成全域性變數就可以。

import multiprocessing


def download(q):
    global li
    li = [1, 2, 3]
    for item in li:
        q.put(item)


def analysis(q):
    '''資料處理'''
    data = []
    while True:
        print(q.qsize())
        q_data = q.get()
        data.append(q_data)
        if len(data) == len(li):
            break
    print(data)


def main():
    # 建立一個佇列
    q = multiprocessing.Queue()
    download(q)
    analysis(q)


if __name__ == '__main__':
    main()

相關文章