日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python --- 线程

發布時間:2023/12/10 python 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python --- 线程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 多任務 - 線程

參考
首先考慮一個沒有多任務的程序:

import timedef sing():# 唱歌 5 秒鐘for i in range(5):print("-----菊花臺ing....-----")time.sleep(1)def dance():# 跳舞 5秒鐘for i in range(5):print("-----跳舞.....-----")time.sleep(5)def main():sing()dance()if __name__ == "__main__":main()

此時,你想同時執行唱歌、跳舞是無法做到的。如果想要同時執行,可以使用python提供的Thread來完成.

import time from threading import Threaddef sing():# 唱歌 5 秒鐘for i in range(5):print("-----菊花臺ing....-----")time.sleep(1)def dance():# 跳舞 5秒鐘for i in range(5):print("-----跳舞.....-----")time.sleep(1)def main():t1 = Thread(target=sing)t2 = Thread(target=dance)t1.start()t2.start()if __name__ == "__main__":main()

關鍵點:

  • from threading import Thread: 從threading包鐘導入Thread
  • t1 = Thread(target=sing): 使用這個將函數變為線程執行的函數~

1.1 多任務的概念

上面體驗了如何同時執行2個異步函數~下面補充一下多任務的概念.

簡單地說,就是操作系統可以同時運行多個任務.例如: 一邊逛瀏覽器,一遍聽音樂

單核CPU執行多任務: 單核CPU執行多任務的關鍵在于,將cpu的時間切片. 任務1執行0.01秒,然后任務2執行0.01秒,在切到任務1執行0.01秒。由于CPU的運算速度很快,因此,我們感覺就行所有任務都在同時執行一樣

多核CPU執行多任務: 真的并行執行多任務只能在多核CPU上實現. 但是,在平常的代碼中,任務數量會遠遠的大于CPU的核心數,因此操作系統會將任務輪流調度到各個核心上執行~

并行: 同一時刻真正運行在不同的CPU上的任務

并發: 在一個很短的時間內,利用CPU的告訴運轉.執行多個任務

1.2 查看當前線程數量

在某些情況下,需要查看當前程序中的線程數量,可以使用threading.enumerate()進行嘗試

import threading from time import sleep, ctimedef sing():for i in range(3):print("正在唱第%d首哥" % i)sleep(2)def dance():for i in range(3):print("正在跳第%d支舞" %i)sleep(2)if __name__ == "__main__":print("開始時間: %s" % ctime())t1 = threading.Thread(target=sing)t2 = threading.Thread(target=dance)t1.start()t2.start()while True:length = threading.enumerate()print("當前的總線程數為: %d" % length)if length <= 1:breaksleep(1)print("結束時間: %s" % ctime())

注意:

  • 當調用Thread的時候,不會創建線程
  • 當調用Thread創建出來的實例對象的 start方法時,才會創建線程以及讓這個線程開始運行

1.3 創建線程的第二種方法

第一種方法是通過: t = Thread(target = 函數名)來準備, t.start()來啟動

第二種方法是通過類繼承threading.Thread來實現,代碼如下:

import threading import timeclass MyThread(threading.Thread):def run(self):for i in range(3):time.sleep(1)msg = "I`m " + self.name + " @" + str(i)print(msg)if __name__ == "__main__":t = MyThread()t.start()

說明:

  • 類的繼承: class MyThread(threading.Thread)
  • 通過類的方式創建的線程,必須在該類中定義run函數. 這樣當調用t.start()時候,新創建的線程會去類中尋找run函數并執行
  • 一個t=MyThread()只會準備一個線程,當t.start()時,會創建線程~
  • 1.4 線程共享全局變量

    有些時候,多個不同的線程可能需要用到同一個變量,下面演示全局變量在多線程中的使用

    from threading import Thread import timedef work1():global g_numfor i in range(3):g_num += 1print("---- in work1, g_num is %d ---" % g_num)def work2():global g_numprint("---- in work2, g_num is %d ---" % g_num)def main():g_num = 100print("---- 線程創建執行之前 g_num is %d ---" % g_num)t1 = Thread(target=work1)t1.start()# 讓 t1線程先執行time.sleep(1)t2 = Thread(target=work2)t2.start()if __name__ == "__main__":main()

    注意:

    • 在一個函數中對全局變量進行修改的時候需要看是否對全局變量的指向進行了修改
      • 如果修改了指向,那么必須使用global
      • 如果僅修改了指向中的數據,則可以省略global

    1.5 帶參數的線程調用

    在調用的時候,可能需要傳遞參數進去.這就需要在線程準備的時候,使用args傳遞參數

    from threading import Thread from time import sleepdef test1(tmp):tmp.append(33)def test2(tmp):tmp.append(66)def main():num_arr = [11, 22]print(str(num_arr))t1 = Thread(target=test1, args=(num_arr,))t2 = Thread(target=test2, args=(num_arr,))t1.start()sleep(1)print(str(num_arr))t2.start()print(str(num_arr))if __name__ == "__main__":main()

    注意:

    • 多任務共享數據的原因: 多個任務合作同時完成一個大任務~
      • 一個任務獲取數據
      • 一個任務處理數據
      • 一個任務發送數據

    1.6 資源競爭

    共享變量會產生一個資源競爭的問題: 多個線程同時對一個全局變量進行修改~下面復現問題

    import threading import timeg_num = 0def test1(num):global g_numfor i in range(num):g_num += 1print("test1: g_num: %d" % g_num)def test2(num):global g_numfor i in range(num):g_num += 1print("test2: g_num: %d" % g_num)def main():t1 = threading.Thread(target=test1, args=(1000000,))t2 = threading.Thread(target=test2, args=(1000000,))t1.start()t2.start()time.sleep(5)print("main: g_num: %d" % g_num)if __name__ == "__main__":main()

    test2: g_num: 1042014
    test1: g_num: 1080242
    main: g_num: 1080242

    以上的原因如下:

    • 假設:

      • t1代表線程1,t2代表線程2
      • g_num +=1 可分解成下面3個步驟:
        • 獲取 g_num的值, 記為t1.1(t2.1)
        • 將g_num的值加1, 記為t1.2(t2.2)
        • 將加1后的值存入g_num, 記為t1.3(t2.3)
    • 下面模擬執行步驟:(根據CPU的特性,分時執行)

      • 假設先執行t1.1
      • 再執行t1.2
      • 然后執行t2.1, 此時重新獲取g_num的值
      • 然后執行t1.3, 此時g_num的值并未改變

    1.7 同步

    以上問題可以通過線程同步來解決,在此之前,需要先了解互斥鎖:

    • 當多個線程幾乎同時修改某一個共享數據的時候,需要進行同步控制
    • 互斥鎖未資源引入了一個狀態: 鎖定/非鎖定
    • 某個線程要更改共享數據的時候,先將其鎖定,此時資源的狀態為"鎖定",其他線程不能更改;知道該線程釋放資源,將資源的狀態變為"非鎖定",其他的線程才能再次鎖定該資源?;コ怄i保證了每次只有一個線程進行寫入操作,從而保證了多線程情況下數據的正確性

    下面是互斥鎖的基本使用:

    # 創建鎖 mutex = threading.Lock()# 鎖定 mutex.acquire()# 釋放 mutex.release()

    注意:

    • 如果這個鎖之前是沒有上鎖得,那么acquire不會堵塞
    • 如果在調用acquire之前已經被上鎖了,那么acquire將會被阻塞直至release釋放

    具體做法如下:

    import threading import timedef test1(num, ):global g_num, metexfor i in range(num):metex.acquire()g_num += 1metex.release()print("test1: g_num: %d" % g_num)def test2(num, ):global g_num, metexfor i in range(num):metex.acquire()g_num += 1metex.release()print("test2: g_num: %d" % g_num)g_num = 0 metex = threading.Lock() def main():# 創建互斥鎖t1 = threading.Thread(target=test1, args=(1000000,))t2 = threading.Thread(target=test2, args=(1000000,))t1.start()t2.start()time.sleep(5)print("main: g_num: %d" % g_num)if __name__ == "__main__":main()

    說明:

    • 全局變量中創建一個互斥鎖: metex = threading.Lock()
    • 在原子代碼前面添上: metex.acquire()
      • 原子代碼: 即要么一次性全部執行,要么不執行的不可分割的代碼
    • 在原子代碼后面添上: metex.release()

    1.8 死鎖

    如果兩個線程分別占用一部分資源,并且同時等待對方的資源,就會造成死鎖的現象。下面使用python實現一個簡單的死鎖程序:

    import threading import timeclass MyThread1(threading.Thread):def run(self):# 線程1 假設下面都是原子代碼mutexA.acquire()print(self.name + "do1 up")time.sleep(1)mutexB.acquire()print(self.name + "do1 down")mutexB.release()mutexA.release()class MyThread2(threading.Thread):def run(self):mutexB.acquire()print(self.name + "do2 up")time.sleep(1)mutexA.acquire()print(self.name + "do2 down")mutexA.release()mutexB.release()mutexA = threading.Lock() mutexB = threading.Lock()def main():t1 = MyThread1()t2 = MyThread2()t1.start()t2.start()if __name__ == "__main__":main()

    說明:

    • 進入線程1,將mutexA鎖定,然后休眠1秒
    • 進入線程2,將mutexB鎖定,然后休眠1秒
    • 之后同時在線程1和2中各自獲取mutexB,mutexA而進入相互等待階段,即死鎖。

    總結

    以上是生活随笔為你收集整理的python --- 线程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。