01.recode
一個(gè)進(jìn)程默認(rèn)有一個(gè)線程确镊,這個(gè)線程叫主線程剑刑。默認(rèn)情況下脚曾,所有的代碼都是在主線程中執(zhí)行的
import time, datetime
def download(film_name):
print('開始下載:%s' % film_name, datetime.datetime.now())
time.sleep(5) # 程序執(zhí)行到這個(gè)地方镣煮,線程會(huì)阻塞5秒(停5秒)安券,再執(zhí)行后面的代碼
print('%s下載結(jié)束' % film_name, datetime.datetime.now())
if __name__ == '__main__':
# 在主線程中下載兩個(gè)電影
download('小黃人')
download('地心游記')
開始下載:小黃人 2018-11-29 17:27:40.037304
小黃人下載結(jié)束 2018-11-29 17:27:45.037590
開始下載:地心游記 2018-11-29 17:27:45.037590
地心游記下載結(jié)束 2018-11-29 17:27:50.037876
02.多線程1
python中提供了threading模塊,來(lái)支持多線程技術(shù)
默認(rèn)創(chuàng)建的線程叫主線程昧绣,其他的線程叫子線程规肴。如果希望代碼在子線程中執(zhí)行,必須手動(dòng)創(chuàng)建線程對(duì)象
import threading
import time, datetime
def download(film_name):
print('開始下載:%s' % film_name, datetime.datetime.now())
time.sleep(5) # 程序執(zhí)行到這個(gè)地方夜畴,線程會(huì)阻塞5秒(停5秒)拖刃,再執(zhí)行后面的代碼
print('%s下載結(jié)束' % film_name, datetime.datetime.now())
print('下載%s:'%film_name,threading.current_thread())
開始下載:小黃人 2018-11-29 17:31:54.387852
開始下載:地心游記 2018-11-29 17:31:54.388852
地心游記下載結(jié)束 2018-11-29 17:31:59.389138
下載地心游記: <Thread(Thread-2, started 7656)>
小黃人下載結(jié)束 2018-11-29 17:31:59.389138
下載小黃人: <Thread(Thread-1, started 7280)>
03.練習(xí)
import requests
from threading import Thread
def download(url: str):
"""下載函數(shù)"""
image_data = requests.get(url).content
file_name = url.split('/')[-1]
with open('images/'+file_name, 'wb') as f:
f.write(image_data)
# 1.下載接口對(duì)應(yīng)的數(shù)據(jù)
# url = 'https://www.apiopen.top/satinApi?type=1&page=1'
# data_dict = requests.get(url).json()
# datas = data_dict['data']
# for dict1 in datas:
# # 拿到一個(gè)圖片地址,就為它創(chuàng)建一個(gè)線程對(duì)象贪绘,用來(lái)在子線程中下載這張圖片
# t = Thread(target=download, args=(dict1['profile_image'],))
# t.start()
#
# print('下載完成')
# 使用正則獲取數(shù)據(jù)
import re
url = 'https://www.apiopen.top/satinApi?type=1&page=1'
text = requests.get(url).text
all_profile_image = re.findall(r'"profile_image":"(.+?)",', text)
for image_url in all_profile_image:
Thread(target=download, args=(image_url,)).start()
04.線程類的子類
from threading import Thread, current_thread
創(chuàng)建子線程兑牡,除了直接創(chuàng)建Thread的對(duì)象,還可以創(chuàng)建這個(gè)類的子類對(duì)象
注意:一個(gè)進(jìn)程中有多個(gè)線程税灌,進(jìn)程會(huì)在所有的線程都結(jié)束才會(huì)結(jié)束;線程中的任務(wù)執(zhí)行完了均函,線程就結(jié)束
# 1.聲明一個(gè)類繼承Thread
class DownloadThread(Thread):
# 想要給run方法傳遞數(shù)據(jù)亿虽,通過(guò)添加對(duì)象屬性來(lái)傳
def __init__(self, file_name):
super().__init__()
self.file_name = file_name
# 2.重寫run方法
def run(self):
# 這個(gè)方法中的代碼會(huì)在子線程中執(zhí)行
print('開始下載: %s' % self.file_name)
# 3.創(chuàng)建線程對(duì)象
t1 = DownloadThread('小黃人')
# 4.通過(guò)線程對(duì)象調(diào)用start在子線程中執(zhí)行run方法
t1.start()
# t1.run() # 直接調(diào)用run方法,會(huì)在主線程中執(zhí)行
開始下載: 小黃人
05.join函數(shù)
from threading import Thread
import time,datetime,random
線程對(duì)象.join() - 等待線程對(duì)象中的任務(wù)執(zhí)行完成
class Download(Thread):
def __init__(self, film_name):
super().__init__()
self.film_name = film_name
def run(self):
print('開始下載:%s' % self.film_name)
a = random.randint(5, 12)
time.sleep(a)
print('%s開始結(jié)束' % self.film_name, '耗時(shí)%d秒' % a)
if __name__ == '__main__':
t1 = Download('小黃人')
t2 = Download('地心游記')
time1 = time.time()
t1.start()
t2.start()
# t1和t2中的任務(wù)都執(zhí)行完成后才執(zhí)行后面的代碼
t1.join()
t2.join()
time2 = time.time()
print('總共時(shí)間:', time2 - time1)
開始下載:小黃人
開始下載:地心游記
小黃人開始結(jié)束 耗時(shí)9秒
地心游記開始結(jié)束 耗時(shí)9秒
總共時(shí)間: 9.001514673233032
06.數(shù)據(jù)共享
import time, threading
注意:當(dāng)多個(gè)線程同時(shí)對(duì)同一個(gè)數(shù)據(jù)進(jìn)行操作的時(shí)候苞也,可能會(huì)出現(xiàn)數(shù)據(jù)混亂
多個(gè)線程對(duì)一個(gè)數(shù)據(jù)進(jìn)行操作洛勉,一個(gè)線程將數(shù)據(jù)讀出來(lái),還沒來(lái)得及存進(jìn)去如迟;
另一個(gè)線程又去讀了占键,這個(gè)時(shí)候就可能產(chǎn)生數(shù)據(jù)安全隱患 - 解決問(wèn)題的方案就是加鎖
Thread - 線程類(創(chuàng)建子線程)
Lock - 鎖(創(chuàng)建鎖對(duì)象)
class Account(object):
def __init__(self, balance, name):
self.balance = balance # 余額
self.name = name
self.lock = threading.Lock() # 創(chuàng)建鎖對(duì)象
def save(self, num):
"""存錢"""
print('開始存錢')
# 加鎖
self.lock.acquire()
old_balance = self.balance
time.sleep(5)
self.balance = old_balance + num
print('存錢成功癣籽!')
# 解鎖
self.lock.release()
def draw(self, num):
"""取錢"""
# 加鎖
print('開始取錢!')
self.lock.acquire()
old_balance = self.balance
time.sleep(4)
self.balance = old_balance - num
print('取錢成功!')
# 解鎖
self.lock.release()
acount1 = Account(1000, '余婷')
# 支付寶存錢
t1 = threading.Thread(target=acount1.save, args=(1000,))
# 銀行卡取錢
t2 = threading.Thread(target=acount1.draw, args=(500,))
t1.start()
t2.start()
t1.join()
t2.join()
print(acount1.balance)
開始存錢
開始取錢!
存錢成功敬辣!
取錢成功!
1500
07.多線程(一個(gè)服務(wù)器與多個(gè)客戶端)
服務(wù)器:
"""服務(wù)器"""
import socket
from threading import Thread
class ConversationThread(Thread):
def __init__(self, conversation, addr):
super().__init__()
self.conversation = conversation
self.addr = addr
def run(self):
while True:
message = self.conversation.recv(1024).decode('utf-8')
print(self.addr, ': ' + message, sep='')
server = socket.socket()
server.bind(('10.7.187.149', 9001))
server.listen(512)
while True:
conversation, addr = server.accept()
# 給服務(wù)器發(fā)送請(qǐng)求的客戶端建立的連接創(chuàng)建一個(gè)子線程达传。在子線程中去處理每個(gè)請(qǐng)求
t = ConversationThread(conversation, addr)
t.start()
客戶端:
"""客戶端"""
import socket
client = socket.socket()
client.connect(('10.7.187.149', 9000))
while True:
message = input('自己:')
client.send(message.encode('utf-8'))