day38-并發(fā)編程之多線程

一、線程

線程顧名思義,就是一條流水線工作的過程,一條流水線必須屬于一個(gè)車間,一個(gè)車間的工作過程是一個(gè)進(jìn)程

車間負(fù)責(zé)把資源整合到一起,是一個(gè)資源單位,而一個(gè)車間內(nèi)至少有一個(gè)流水線

流水線的工作需要電源,電源就相當(dāng)于cpu

所以,進(jìn)程只是用來把資源集中到一起(進(jìn)程只是一個(gè)資源單位,或者說資源集合),而線程才是cpu上的執(zhí)行單位。

多線程(即多個(gè)控制線程)的概念是,在一個(gè)進(jìn)程中存在多個(gè)控制線程,多個(gè)控制線程共享該進(jìn)程的地址空間,相當(dāng)于一個(gè)車間內(nèi)有多條流水線,都共用一個(gè)車間的資源。

進(jìn)程是資源分配的最小單位,線程是CPU調(diào)度的最小單位。每一個(gè)進(jìn)程中至少有一個(gè)線程。

二、開啟線程的兩種方式

method 1 :

from threading import Thread
import time


def task(n):
    print(f"線程{n}開始")
    time.sleep(2)
    print(f"線程{n}結(jié)束")


if __name__ == '__main__':
    t = Thread(target=task,args=(1,))
    t.start()
    t2 = Thread(target=task, args=(2,))
    t2.start()
    print("主")

method 2 :

from threading import Thread
import time


class MyThread(Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self) -> None:
        print(f"進(jìn)程{self.name}開始")
        time.sleep(2)
        print(f"進(jìn)程{self.name}結(jié)束")


if __name__ == '__main__':
    t = MyThread("1")
    t.start()
    t1 = MyThread("2")
    t1.start()
    t.join()
    t1.join()
    print("主")

三、統(tǒng)一進(jìn)程下多線程數(shù)據(jù)共享

from threading import Thread

import time

money = 99


def task(n):
    global money
    money=n
    print('開始')
    # time.sleep(n)
    print('結(jié)束')


if __name__ == '__main__':
    t = Thread(target=task, args=(2,))
    t.start()

    t1 = Thread(target=task, args=(66,))
    t1.start()


    t.join()
    t1.join()
    print(money)
    print('主')

線程相關(guān)的其他方法:

Thread實(shí)例對(duì)象的方法:

  • isAlive(): 返回線程是否活動(dòng)的。
  • getName(): 返回線程名。
  • setName(): 設(shè)置線程名。

threading模塊提供的一些方法:

  • threading.currentThread(): 返回當(dāng)前的線程變量。
  • threading.enumerate(): 返回一個(gè)包含正在運(yùn)行的線程的list。正在運(yùn)行指線程啟動(dòng)后、結(jié)束前,不包括啟動(dòng)前和終止后的線程。
  • threading.activeCount(): 返回正在運(yùn)行的線程數(shù)量,與len(threading.enumerate())有相同的結(jié)果。

四、守護(hù)線程

無論是進(jìn)程還是線程,都遵循:守護(hù)xxx會(huì)等待主xxx運(yùn)行完畢后被銷毀

需要強(qiáng)調(diào)的是:運(yùn)行完畢并非終止運(yùn)行

  • 1.對(duì)主進(jìn)程來說,運(yùn)行完畢指的是主進(jìn)程代碼運(yùn)行完畢

  • 2.對(duì)主線程來說,運(yùn)行完畢指的是主線程所在的進(jìn)程內(nèi)所有非守護(hù)線程統(tǒng)統(tǒng)運(yùn)行完畢,主線程才算運(yùn)行完畢

詳細(xì)解釋:

  • 1 主進(jìn)程在其代碼結(jié)束后就已經(jīng)算運(yùn)行完畢了(守護(hù)進(jìn)程在此時(shí)就被回收),然后主進(jìn)程會(huì)一直等非守護(hù)的子進(jìn)程都運(yùn)行完畢后回收子進(jìn)程的資源(否則會(huì)產(chǎn)生僵尸進(jìn)程),才會(huì)結(jié)束。

  • 2 主線程在其他非守護(hù)線程運(yùn)行完畢后才算運(yùn)行完畢(守護(hù)線程在此時(shí)就被回收)。因?yàn)橹骶€程的結(jié)束意味著進(jìn)程的結(jié)束,進(jìn)程整體的資源都將被回收,而進(jìn)程必須保證非守護(hù)線程都運(yùn)行完畢后才能結(jié)束。

五、GIL全局解釋器鎖

在Cpython解釋器中,同一個(gè)進(jìn)程下開啟的多線程,同一時(shí)刻只能有一個(gè)線程執(zhí)行,無法利用多核優(yōu)勢(shì)。

  1. python的解釋器有很多,cpython,jpython,pypy(python寫的解釋器)

  2. python的庫多,庫都是基于cpython寫起來的,其他解釋器沒有那么多的庫

  3. cpython中有一個(gè)全局大鎖,每條線程要執(zhí)行,必須獲取到這個(gè)鎖

  4. 這個(gè)鎖存在的原因是因?yàn)閜ython的垃圾回收機(jī)制

  5. python的多線程其實(shí)就是單線程

  6. 某個(gè)線程想要執(zhí)行,必須先拿到GIL,我們可以把GIL看作是“通行證”,并且在一個(gè)python進(jìn)程中,GIL只有一個(gè)。拿不到通行證的線程,就不允許進(jìn)入CPU執(zhí)行

  7. 總結(jié):cpython解釋器中有一個(gè)全局鎖(GIL),線程必須獲取到GIL才能執(zhí)行,我們開的多線程,不管有幾個(gè)cpu,同一時(shí)刻,只有一個(gè)線程在執(zhí)行(python的多線程,不能利用多核優(yōu)勢(shì))

  8. 如果是io密集型操作:開多線程

  9. 如果是計(jì)算密集型:開多進(jìn)程

更詳細(xì)的介紹

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容