進(jìn)程和線程
1.什么是進(jìn)程
- 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序
- 每個進(jìn)程之間是獨(dú)立的,每個進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)
2.什么是線程
- 一個進(jìn)程想要執(zhí)行任務(wù),必須有線程(每個進(jìn)程至少要有1條線程)
- 一個進(jìn)程(程序)的所有任務(wù)都在線程中執(zhí)行
3.什么是多線程
- 1個進(jìn)程中可以開啟多條線程,每條線程可以并行執(zhí)行不同的任務(wù)
- 多線程技術(shù)可以提高程序的執(zhí)行效率
4.多線程的原理
- 同一個時間,CPU只能處理一條線程,只有1條線程在執(zhí)行
- 多線程并發(fā)執(zhí)行,其實(shí)是cpu快速的在多條線程之間調(diào)度
- 如果CPU調(diào)度線程的事件足夠快,就造成了多線程并發(fā)執(zhí)行的假象
耗時操作
1.耗時操作放到主線程中的問題
- 耗時操作放到主線程中會阻塞線程
- 多個耗時操作都放到一個線程中執(zhí)行,最終執(zhí)行時間是多個耗時操作時間和
2.怎么解決
- 使用多線程(創(chuàng)建多個線程)
多線程技術(shù)
python內(nèi)置的threading模塊,可以支持多線程
所有的進(jìn)程默認(rèn)都有一個線程(一般叫這個線程為主線程),其他的線程叫子線
如果想要在進(jìn)程中添加其他的線程,就創(chuàng)建線程對象
import threading
import time
def download(file):
print('開始下載', file)
time.sleep(3)
print(file, '下載結(jié)束')
if __name__ == '__main__':
print('aaaa')
# 1.創(chuàng)建線程對象
target:需要在子線程中執(zhí)行的函數(shù)
args:調(diào)用函數(shù)的實(shí)參列表(參數(shù)類型必須是列表)
t1 = threading.Thread(target=download, args=['愛情公寓'])
# 2.在子線程中執(zhí)行任務(wù)
t1.start()
t2 = threading.Thread(target=download, args=['allalala'])
t2.start()
print('66666666666')
time.sleep(5)
- 方式2:寫一個自己的線程類
- 寫一個類,繼承Thread類
- 重寫run方法,在里面規(guī)定需要在子線程中執(zhí)行的任務(wù)
- 在子線程中執(zhí)行的任務(wù)對應(yīng)的功能,如果需要參數(shù),通過對象屬性來傳值
傳值使用對象屬性來傳值
from threading import Thread
from requests import request
import os
import re
# 下載數(shù)據(jù)
class DownloadThread(Thread):
'''下載類'''
def __init__(self, file):
super().__init__()
self.file = file
def run(self):
'''run方法'''
'''
寫在這個方法的內(nèi)容就是在子線程中執(zhí)行的內(nèi)容
這個方法不要直接調(diào)用
'''
print('開始下載')
suffix = re.search(r'[%\\]\w+\.\w+$', self.file).group()
response = request('GET', self.file)
data = response.content
with open('./day02-多線程/' + suffix, 'wb') as f:
f.write(data)
print('下載完成....')
if __name__ == '__main__':
print(os.getcwd())
d1 = DownloadThread(
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533720058151&di=766b5c97653351e805c85881ecaa57d0&imgtype=0&src=http%3A%2F%2Fx.itunes123.com%2Fuploadfiles%2Fb2ab55461e6dc7a82895c7425fc89017.jpg'
)
# 通過start間接調(diào)用run方法,run方法中的任務(wù)在子線程中執(zhí)行
d1.start()
# 直接調(diào)用run方法,run方法在當(dāng)前線程中執(zhí)行
print('哇哈哈哈哈哈哈')
多線程的應(yīng)用
import socket
from threading import Thread
class MsgThread(Thread):
'''在子線程中處理不同的客戶端會話'''
def __init__(self, conver: socket.socket, addr):
# 形參接受時,參數(shù)后面加:進(jìn)行類型說明
# 注意只是說明類型而不是轉(zhuǎn)換類型
super().__init__()
self.conver = conver
self.addr = addr
def run(self):
while True:
self.conver.send('哇哈哈哈'.encode())
print(self.addr, self.conver.recv(1024).decode(encoding='utf-8'))
if __name__ == '__main__':
server = socket.socket()
server.bind(('10.7.181.92', 8080))
server.listen(5)
while True:
conver, addr = server.accept()
c = MsgThread(conver, addr)
c.start()
# while True:
# conver.send('666'.encode())
# print(conver.recv(1024).decode(encoding='utf-8'))
join方法
- 獲取當(dāng)前線程
- 主線程:MainThread
- 子線程:Thread-數(shù)字
currentThread()
- 如果一個任務(wù)想要在另一個子線程結(jié)束后再執(zhí)行,就用這個子線程對象調(diào)用join
- 所以join也會阻塞線程,阻塞到子線程任務(wù)執(zhí)行完成為止
from threading import Thread, currentThread
from random import randint
import time
class Download(Thread):
def __init__(self, file):
super().__init__() # 必須調(diào)用,否則當(dāng)前這個類對象就不是線程
self.file = file
def run(self):
print('開始下載', self.file)
time.sleep(randint(3, 7))
print(currentThread())
print('下載完成', self.file)
if __name__ == '__main__':
# time.time():獲取當(dāng)前的時間戳
start_time = time.time()
t1 = Download('狗屎')
t1.start()
t2 = Download('紅糖')
t2.start()
print('.............')
# 獲取當(dāng)前線程
'''
主線程:MainThread
子線程:Thread-數(shù)字
'''
print(currentThread())
# 如果一個任務(wù)想要在另一個子線程結(jié)束后再執(zhí)行,就用這個子線程對象調(diào)用join
# 所以join也會阻塞線程,阻塞到子線程任務(wù)執(zhí)行完成為止
t1.join()
end_time = time.time()
print('耗時%.2f' % (end_time - start_time))
t2.join()
end_time = time.time()
print('總共耗時%.2f' % (end_time - start_time))