程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

「Python入門」Python多線程

編輯:Python

活動地址:CSDN21天學習挑戰賽

文章目錄

  • 前言
  • 一、線程與進程的區別
  • 二、多線程的使用方式
    • 2.1 直接使用
    • 2.2 繼承式調用
  • 三、 守護線程
  • 四、 join線程同步
  • 五、 線程鎖(互斥鎖Mutex)
  • 六、 RLock(遞歸鎖)
  • 七、 線程池


前言

線程 也叫輕量級進程,是操作系統能夠進行運算調度的最小單位,它被包涵在進程之中,是進程中的實際運作單位。線程自己不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其他線程共享進程所擁有的全部資源。一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以並發執行。
多線程 線程在程序中是獨立的、並發的執行流。與分隔的進程相比,進程中線程之間的隔離程度要小,它們共享內存、文件句柄和其他進程應有的狀態。

  • 進程之間不能共享內存,但線程之間共享內存非常容易。
  • 操作系統在創建進程時,需要為該進程重新分配系統資源,但創建線程的代價則小得多。因此使用多線程來實現多任務並發執行比使用多進程的效率高。
  • python語言內置了多線程功能支持,而不是單純地作為底層操作系統的調度方式,從而簡化了python的多線程編程。


一、線程與進程的區別

  • 線程共享內存,進程獨立內存
  • 線程啟動速度塊,進程啟動速度慢,運行時速度沒有可比性
  • 同一個進程的線程間可以直接交流,兩個進程想通信,必須通過一個中間代理來實現
  • 創建新線程很簡單,創建新進程需要對其父進程進行一次克隆
  • 一個線程可以控制和操作同一線程裡的其他線程,但是進程只能操作子進程

二、多線程的使用方式

2.1 直接使用

# 文件名 python1.py
# -*- coding:utf-8 -*-
# 線程直接使用
import threading
import time
# 需要多線程運行的函數
def fun(args):
print("我是線程%s" % args)
time.sleep(2)
print("線程%s運行結束" % args)
# 創建線程
t1 = threading.Thread(target=fun, args=(1,))
t2 = threading.Thread(target=fun, args=(2,))
start_time = time.time()
t1.start()
t2.start()
end_time = time.time()
print("兩個線程一共的運行時間為:", end_time-start_time)
print("主線程結束")
""" 執行 python python1.py 運行結果: 我是線程1 我是線程2兩個線程一共的運行時間為: 0.0019996166229248047 主線程結束 線程1運行結束 線程2運行結束 """

2.2 繼承式調用

# 文件名 python2.py
# 繼承式調用
import threading
import time
class MyThreading(threading.Thread):
def __init__(self, name):
super(MyThreading, self).__init__()
self.name = name
# 線程要運行的代碼
def run(self):
print("我是線程%s" % self.name)
time.sleep(2)
print("線程%s運行結束" % self.name)
t1 = MyThreading(1)
t2 = MyThreading(2)
start_time = time.time()
t1.start()
t2.start()
end_time = time.time()
print("兩個線程一共的運行時間為:", end_time-start_time)
print("主線程結束")
""" 執行 python python2.py 運行結果: 我是線程1 我是線程2 兩個線程一共的運行時間為: 0.0010724067687988281 主線程結束 線程2運行結束 線程1運行結束 """

三、 守護線程

  • 在Python多線程編程中,join方法的作用式線程同步。
  • 守護線程,是為守護別人而存在的,當設置為守護線程後,被守護的主線程不存在後,守護線程也自然不存在。

Python多線程默認情況(設置線程setDaemon(False)),主線程執行完自己的任務後,就退出了,此時子線程會繼續執行自己的任務,直到子線程任務結束
代碼演示:threading中的兩個創建多線成的例子都是。

# 守護線程
import threading
import time
class MyThreading(threading.Thread):
def __init__(self, name):
super(MyThreading, self).__init__()
self.name = name
# 線程要運行的代碼
def run(self):
print("我是線程%s" % self.name)
time.sleep(2)
print("線程%s運行結束" % self.name)
t1 = MyThreading(1)
t2 = MyThreading(2)
start_time = time.time()
t1.setDaemon(True)
t1.start()
t2.setDaemon(True)
t2.start()
end_time = time.time()
print("兩個線程一共的運行時間為:", end_time-start_time)
print("主線程結束")
""" 執行 python python3.py 後續執行結果以截圖的形式呈現,文件名可自定義為xx.py,執行 python xx.py 指令即可. """

四、 join線程同步

當不給程序設置守護進程時,主程序將一直等待子程序全部運行完成才結束

# join:線程同步
import threading
import time
class MyThreading(threading.Thread):
def __init__(self, name):
super(MyThreading, self).__init__()
self.name = name
# 線程要運行的代碼
def run(self):
print("我是線程%s" % self.name)
time.sleep(3)
print("線程%s運行結束" % self.name)
threading_list = []
start_time = time.time()
for x in range(50):
t = MyThreading(x)
t.start()
threading_list.append(t)
for x in threading_list:
x.join() # 為線程開啟同步
end_time = time.time()
print("50個線程一共的運行時間為:", end_time-start_time)
print("主線程結束")

五、 線程鎖(互斥鎖Mutex)

一個進程下可以啟用多個線程,多個線程共享父進程的內存空間,也就意味著每個線程可以訪問同一份數據。

# 線程鎖(互斥鎖Mutex)
# -*- coding:utf8 -*-
import threading
import time
num = 100
threading_list = []
def fun():
global num
print("get num:", num)
num += 1
time.sleep(1)
for x in range(200):
t = threading.Thread(target=fun)
t.start()
threading_list.append(t)
for x in threading_list:
x.join()
print("nun:", num)

六、 RLock(遞歸鎖)

# RLock(遞歸鎖)
import threading, time
def run1():
lock.acquire()
print("grab the first part data")
global num
num += 1
lock.release()
return num
def run2():
lock.acquire()
print("grab the second part data")
global num2
num2 += 1
lock.release()
return num2
def run3():
lock.acquire()
res = run1()
print('--------between run1 and run2-----')
res2 = run2()
lock.release()
print(res, res2)
if __name__ == '__main__':
num, num2 = 0, 0
lock = threading.RLock()
for i in range(3):
t = threading.Thread(target=run3)
t.start()
while threading.active_count() != 1:
print(threading.active_count())
else:
print('----all threads done---')
print(num, num2)

注:在開發的過程中要注意有些操作默認都是 線程安全的(內部集成了鎖的機制),我們在使用的時無需再通過鎖再處理

# RLock(遞歸鎖)
import threading
data_list = []
lock_object = threading.RLock()
def task():
print("開始")
for i in range(1000000):
data_list.append(i)
print(len(data_list))
for i in range(2):
t = threading.Thread(target=task)
t.start()

七、 線程池

線程不是開的越多越好,開的多了可能會導致系統的性能更低了。

# 線程池
import time
from concurrent.futures import ThreadPoolExecutor # 並行期貨,線程池執行者
""" pool = ThreadPoolExecutor(100) pool.submit(函數名,參數1,參數2,參數...) """
def task(video_url, num):
print("開始執行任務", video_url, num) # 開始執行任務 www.vitian-99.vip 3
time.sleep(1)
# 創建線程池,最多維護10個線程
threadpool = ThreadPoolExecutor(10)
# 生成100網址,並放入列表
url_list = ["www.vitian-{}.vip".format(i) for i in range(100)]
for url in url_list:
""" 在線程池中提交一個任務,線程池如果有空閒線程,則分配一個線程去執行,執行完畢後在將線程交還給線程池, 如果沒有空閒線程,則等待。注意在等待時,與主線程無關,主線程依然在繼續執行。 """
threadpool.submit(task, url, 3)
print("等待線程池中的任務執行完畢中······")
threadpool.shutdown(True) # 等待線程池中的任務執行完畢後,在繼續執行
print("END")

# 線程池的回調
import time
import random
from concurrent.futures import ThreadPoolExecutor
def task(video_url):
print("開始執行任務", video_url)
time.sleep(1)
return random.randint(0, 10) # 將結果封裝成一個Futuer對象,返回給線程池
def done(response): # response就是futuer對象,也就是task的返回值分裝的一個Futuer對象
print("任務執行完後,回調的函數", response.result()) # 即Futuer.result():取出task的返回值
# 創建線程池
threadpool = ThreadPoolExecutor(10)
url_list = ["www.xxxx-{}.com".format(i) for i in range(5)]
for url in url_list:
futuer = threadpool.submit(task, url) # futuer是由task返回的一個Future對象,裡面有記錄task的返回值
futuer.add_done_callback(done) # 回調done函數,執行者依然是子線程
# 優點:可以做分工,例如:task專門下載,done專門將下載的數據寫入本地文件。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved