線程和進程

線程和進程

多任務介紹

1.現實中的多任務

看着番劇吃着飯

2.計算機中的多任務

計算機中的多任務是指,操作系統同時完成多項任務的處理。此處,同時是指同一個時間段內,而非某一個瞬時時間點。

多任務處理是指,用戶在同一時間段內運行多個應用程序,每個應用程序就可以稱之爲一個任務。

現在,多核CPU已經非常普及了,但是,即使過去的單核CPU,也可以執行多任務。由於CPU執行代碼都是順序執行的,那麼,單核CPU是怎麼執行多任務的呢?
答案就是操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒……這樣反覆執行下去。表面上看,每個任務都是交替執行的,但是,由於CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
真正的並行執行多任務只能在多核CPU上實現,但是,由於任務數量遠遠多於CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。

併發與並行

  • 併發處理(concurrency Processing):指一個時間段中有幾個程序都處於已啓動運行到運行完畢之間,且這幾個程序都是在同一個處理機(CPU)上運行,但任一個時刻點上只有一個程序在處理機(CPU)上運行
  • 並行處理(Parallel Processing):是計算機系統中能同時執行兩個或更多個處理的一種計算方法。並行處理可同時工作於同一程序的不同方面。並行處理的主要目的是節省大型和複雜問題的解決時間。

併發的關鍵是你有處理多個任務的能力,不一定要同時。並行的關鍵是你有同時處理多個任務的能力。所以說,並行是併發的子集

[外鏈圖片轉存失敗(img-XQ5DSZh6-1565087335893)(C:\Users\11980\Desktop\學習\python\python學習筆記\暑假班\每日筆記\併發並行.png)]

進程

進程:一個程序的執行實例。每個進程提供執行程序所需的所有資源。

進程本質上是資源的集合。

一個進程有虛擬的地址空間、可執行的代碼、操作系統接口、安全的上下文(記錄啓動該進程的用戶和權限等等)、唯一的進程ID、環境變量、優先級類、最小和最大的工作空間(內存空間),還要至少有一個線程。

進程的創建 —> fork()

python的os模塊封裝了常見的系統調用函數,其中包括fork(),可以讓我們在程序中輕鬆的創建子進程。

import os
pid = os.fork()
if pid ==0:
    print("zucc")
else:
    print("ZUCC")

在Unix/Linux中,提供了fork()系統函數。

  • 普通的函數調用,調用一次,返回一次。
  • fork()調用一次,返回兩次。
    • 因爲操作系統自動把當前的進程(父進程)複製一份(子進程),然後,分別在父進程和子進程內返回。

fork()子進程永遠返回0,而對應的父進程返回子進程的ID。

一個父進程可以fork出很多子進程。父進程可以記下每個子進程的ID,而子進程只需要調用getppid就可以返回父進程ID。

  • getpid()返回當前進程標識
  • getppid()返回父進程標識
import os
pid = os.fork()
if pid < 0:
    print("fork 調用失敗")
elif pid == 0:
    print("我是子進程:\t %s,我的父進程是:\t %s" % (os.getpid(), os.getppid()))
else:
    print("我是父進程:\t %s,我的子進程是:\t %s" % (os.getpid(), pid))
print("父子進程都可以執行這裏。")
# 我是父進程:	 10708,我的子進程是:	 10709
# 父子進程都可以執行這裏。
# 我是子進程:	 10709,我的父進程是:	 10708
# 父子進程都可以執行這裏。

線程

線程是操作系統能夠運算調度的最小單位。線程被包含在進程之中,是進程中實際的運作單位。

一條線程是進程中一個單一順序的控制流,一個進程可以併發多個線程,每條線程併發執行不同的任務。

一條線程是一個execution context(執行上下文),即一個CPU執行時所需要的一串指令。

線程的工作方式
假設你正在讀一本書,沒有讀完,你想休息一下,但是你想在回來時恢復到當時讀的具體進度。有一個方法就是記下頁數、行數與字數這三個數值,這些數值就是execution context。如果你的室友在你休息的時候,使用相同的方法讀這本書。你和她只需要這三個數字記下來就可以在交替的時間共同閱讀這本書了。

線程的工作方式與此類似。CPU會給你一個在同一時間能夠做多個運算的幻覺,實際上它在每個運算上只花了極少的時間,本質上CPU同一時刻只幹了一件事。它能這樣做就是因爲它有每個運算的execution context。就像你能夠和你朋友共享同一本書一樣,多任務也能共享同一塊CPU。

進程的工作方式

進程與線程的區別

  • 同一進程中的線程共享同一內存空間,但是進程之間是獨立的。

  • 同一個進程中的所有線程的數據是共享的,進程之間的數據是獨立的。

  • 對主線程的修改可能影響其他線程的行爲,但是父進程的修改(除了刪除)不會影響其他子進程。

  • 線程是一個上下文的執行指令,而進程則是與運算相關的一簇資源。

  • 同一個進程的線程之間可以直接通信,但是進程之間的交流需要藉助中間代理來實現。

  • 創建新的線程很容易,但是創建新的進程需要對父進程做一次複製。

  • 一個線程可以操作同一進程的其他線程,但是進程只能操作其子進程。

  • 線程啓動速度快,進程啓動速度慢(但是兩者運行速度沒有可比性)。

Python中多線程的實現

  • 創建一個函數傳入Thread對象中
import threading
import time
def download_music():
    for i in range(5):
        time.sleep(1)
        print("正在下載歌曲%d" % i)

def play_music():
    for i in range(5):
        time.sleep(1)
        print("正在播放歌曲%d" % i)
def main():
    # 創建兩個線程對象,target指向新開啓的線程要執行的函數
    t1 = threading.Thread(target=download_music)
    t2 = threading.Thread(target=play_music)

    t1.start()
    t2.start()

if __name__ == "__main__":
    main()
# 正在下載歌曲0
# 正在播放歌曲0
# 正在下載歌曲1
# 正在播放歌曲1
# 正在下載歌曲2
# 正在播放歌曲2
# 正在下載歌曲3
# 正在播放歌曲3
# 正在下載歌曲4
# 正在播放歌曲4

1.可以明顯看出使用多線程併發的操作,花費時間要短很多。

2.當我們調用start()時,纔會真正的執行線程,執行線程中的代碼。

  • 繼承Thread類,創建一個新的class,將要執行的代碼寫到run函數裏

demo:

import threading
import time
# 自定義類,threading.Thread

class MyThread(threading.Thread):
    def run(self):
        for i in range(5):
            time.sleep(1)
            # name 保存的是當前線程的名字
            msg = "I am " + self.name + ' @ ' + str(i)
            print(msg)
if __name__ == '__main__':

    mythread_1 = MyThread()
    mythread_2 = MyThread()

    mythread_1.start()
    mythread_2.start()

out:

I am Thread-2 @ 0
I am Thread-1 @ 0
I am Thread-1 @ 1
I am Thread-2 @ 1
I am Thread-2 @ 2
I am Thread-1 @ 2
I am Thread-2 @ 3
I am Thread-1 @ 3
I am Thread-1 @ 4
I am Thread-2 @ 4

python的threading.Thread類中有一個run方法,用於定義線程的功能函數,可以在自己的線程類中覆蓋該方法。而創建自己的線程實例後,通過Thread的start()方法,可以啓動該線程。當該線程獲得執行機會時,就會調用run()方法執行線程。

注:

threading.currentThread(): 返回當前的線程變量。 
threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啓動後、結束前,不包括啓動前和終止後的線程。 
threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。

線程何時開啓,何時結束

  • 子線程何時開始,何時運行
    • 當調用thread.start()時,開啓線程,再運行線程的代碼。
  • 子線程何時結束
    • 子線程把target指向的函數中的語句執行完畢後,或者線程中的run代碼執行完畢後,立即結束當前子進程。
  • 查看當前的線程數量
    • 通過threading.enumerate()可枚舉當前運行的所有線程
  • 主線程何時結束
    • 所有子線程執行完畢後,主線程才結束

demo:

import threading
import time


def tes1():
    for i in range(5):
        time.sleep(1)
        print("---子線程1---%d" % i)
        print("子線程1中查看線程情況", threading.enumerate())
def tes2():
    for i in range(10):
        time.sleep(1)
        print("---子進程2---%d" % i)
        print("子線程2中查看線程情況", threading.enumerate())

def main():
    # threading.enumerate():枚舉當前的所有線程
    print("創建線程之前的線程情況", threading.enumerate())

    # 創建線程對象
    t1 = threading.Thread(target=tes1)
    t2 = threading.Thread(target=tes2)

    time.sleep(1)
    print("創建線程之後的線程情況", threading.enumerate())

    t1.start()
    t2.start()

    time.sleep(1)
    print("調用了thread.start()之後的線程情況", threading.enumerate())
    t2.join() # 當t2線程執行完後再執行後續的代碼
    print("查看當前線程", threading.enumerate())

if __name__ == "__main__":
    main()

out:

創建線程之前的線程情況 [<_MainThread(MainThread, started 8356)>]
創建線程之後的線程情況 [<_MainThread(MainThread, started 8356)>]
調用了thread.start()之後的線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子線程1---0
---子進程2---0
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
子線程1中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子進程2---1
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子線程1---1
子線程1中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子進程2---2
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子線程1---2
子線程1中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子進程2---3
---子線程1---3
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
子線程1中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子進程2---4
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子線程1---4
子線程1中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-1, started 16904)>, <Thread(Thread-2, started 14500)>]
---子進程2---5
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-2, started 14500)>]
---子進程2---6
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-2, started 14500)>]
---子進程2---7
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-2, started 14500)>]
---子進程2---8
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-2, started 14500)>]
---子進程2---9
子線程2中查看線程情況 [<_MainThread(MainThread, started 8356)>, <Thread(Thread-2, started 14500)>]
查看當前線程 [<_MainThread(MainThread, started 8356)>]

線程的執行順序

import threading
import time
# 自定義類,threading.Thread

class MyThread(threading.Thread):
    def run(self):
        for i in range(5):
            time.sleep(1)
            # name 保存的是當前線程的名字
            msg = "I am " + self.name + ' @ ' + str(i)
            print(msg)
def tes():
    for i in range(3):
        t = MyThread()
        t.start()

if __name__ == '__main__':

    tes()

out:

I am Thread-1 @ 0
I am Thread-2 @ 0
I am Thread-3 @ 0
I am Thread-1 @ 1
I am Thread-2 @ 1
I am Thread-3 @ 1
I am Thread-1 @ 2
I am Thread-2 @ 2
I am Thread-3 @ 2
I am Thread-1 @ 3
I am Thread-2 @ 3
I am Thread-3 @ 3
I am Thread-3 @ 4
I am Thread-1 @ 4
I am Thread-2 @ 4
main__':

    tes()

out:

I am Thread-1 @ 0
I am Thread-2 @ 0
I am Thread-3 @ 0
I am Thread-1 @ 1
I am Thread-2 @ 1
I am Thread-3 @ 1
I am Thread-1 @ 2
I am Thread-2 @ 2
I am Thread-3 @ 2
I am Thread-1 @ 3
I am Thread-2 @ 3
I am Thread-3 @ 3
I am Thread-3 @ 4
I am Thread-1 @ 4
I am Thread-2 @ 4
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章