1.耗時操作
一個線程默認(rèn)有一個線程漱受,這個線程叫主線程,默認(rèn)情況下所有的任務(wù)(代碼)都是在主線程中執(zhí)行的
import time,datetime
def download(film_name):
print("開始下載%s"%film_name,datetime.datetime.now())
time.sleep(5)#程序執(zhí)行到這個地方憨攒,線程會阻塞(停5s),再執(zhí)行后面的代碼
print("%s下載結(jié)束"%film_name,datetime.datetime.now())
if __name__ == '__main__':
download("喜洋洋與灰太狼")
download("花園寶寶")
2.多線程
python中提供了threading模塊
默認(rèn)創(chuàng)建的線程叫主線程阀参,其他的線程叫子線程肝集。如果希望代碼在子線程中執(zhí)行,必須手動創(chuàng)建線程對象蛛壳。
import threading
import time,datetime
def download(film_name):
print("開始下載%s"%film_name,datetime.datetime.now())
time.sleep(5)#程序執(zhí)行到這個地方杏瞻,線程會阻塞(停5s),再執(zhí)行后面的代碼
print("%s下載結(jié)束"%film_name,datetime.datetime.now())
print(threading.current_thread())
if __name__ == '__main__':
# 1.創(chuàng)建線程對象
"""
Thread - 線程類
Thread(target=函數(shù)名,args=參數(shù)列表) - 直接創(chuàng)建線程對象 - 返回線程對象
函數(shù)名 - 需要在當(dāng)前創(chuàng)建的子線程中執(zhí)行的函數(shù)的函數(shù)變量
參數(shù)列表 - 元組衙荐,元組的元素
"""
t1 = threading.Thread(target=download,args=("海綿寶寶",))
t2 = threading.Thread(target=download,args=("花園寶寶",))
# 2.在子線程中執(zhí)行任務(wù)
"""
在這兒就是在調(diào)用ti對應(yīng)的線程中調(diào)用download函數(shù)捞挥,并且傳遞參數(shù)“海綿寶寶”
"""
t1.start()
t2.start()
#打印結(jié)果
"""
開始下載海綿寶寶 2018-11-29 19:16:21.574319
開始下載花園寶寶 2018-11-29 19:16:21.574319
海綿寶寶下載結(jié)束 花園寶寶下載結(jié)束 2018-11-29 19:16:26.5746052018-11-29 19:16:26.574605
<Thread(Thread-2, started 7488)>
<Thread(Thread-1, started 5160)>
"""
3.線程類的子類
創(chuàng)建子線程除了直接創(chuàng)建Thread的對象,還可以創(chuàng)建這個類的子類對象
注意:一個進(jìn)程中有多個線程忧吟,進(jìn)程會在所有的線程結(jié)束才會結(jié)束砌函;
線程中的任務(wù)執(zhí)行完了線程就結(jié)束了
from threading import Thread,current_thread
#1.聲明一個類繼承Thread
class DownloadThread(Thread):
def __init__(self,file_name):
super().__init__()
self.file_name =file_name
#2.重寫run方法
def run(self):
#這個方法中的代碼會在子線程當(dāng)中執(zhí)行
print("開始下載:%s"%self.file_name)
print(current_thread())
# 3.創(chuàng)建線程對象
d1=DownloadThread("滴滴滴")
d1.start()
d2 =DownloadThread("啊啊啊")
d2.start()
# d1.run()#直接調(diào)用run方法,會在主線程中執(zhí)行
"""
打印結(jié)果:
開始下載:滴滴滴
<DownloadThread(Thread-1, started 6960)>
開始下載:啊啊啊
<DownloadThread(Thread-2, started 6432)>
"""
4.join
線程對象.join() - 等待線程中的任務(wù)執(zhí)行完成
from threading import Thread
import time,datetime,random
class Download(Thread):
def __init__(self,file_name):
super().__init__()
self.file_name=file_name
def run(self):
print("開始下載%s"%self.file_name)
a=random.randint(5,12)
time.sleep(a)
print("%s下載結(jié)束"%self.file_name,"耗時%d秒"%a)
if __name__ == '__main__':
t1 =Download("花園寶寶")
t2 =Download("海綿寶寶")
time1 =time.time()
t1.start()
t2.start()
t1.join()
t2.join()
time2 =time.time()
print(time2-time1)
"""
打印結(jié)果:
開始下載花園寶寶
開始下載海綿寶寶
花園寶寶下載結(jié)束 耗時10秒
海綿寶寶下載結(jié)束 耗時11秒
耗時: 11.00162935256958
"""
5.數(shù)據(jù)共享
注意:當(dāng)多個線程同時對同一個數(shù)據(jù)操作的時候溜族,可能會出現(xiàn)數(shù)據(jù)混亂
多個線程對一個數(shù)據(jù)進(jìn)行操作讹俊,一個線程將一個數(shù)據(jù)讀出來,還沒來得及存進(jìn)去煌抒,
另一個線程又去讀了仍劈,這個時候就可能產(chǎn)生數(shù)據(jù)安全隱患
解決方案:加鎖
Thread - 線程類(創(chuàng)建子線程)
Lock - 鎖類 (創(chuàng)建鎖對象)
import time,threading
class Account(object):
def __init__(self,balance,name):
self.balance=balance
self.name =name
self.lock =threading.Lock()#創(chuàng)建鎖對象
def save_money(self,num):
"""存錢"""
self.lock.acquire()#加鎖
print("開始存錢")
old_balance=self.balance
time.sleep(5)
self.balance=old_balance+num
print("存錢成功")
self.lock.release()#解鎖
def pay_money(self,num):
"""取錢"""
self.lock.acquire() # 加鎖
print("開始取錢")
old_balance = self.balance
time.sleep(5)
self.balance = old_balance - num
print("取錢成功")
self.lock.release() # 解鎖
a1 =Account(1000,"小明")
#支付寶存錢
t1 =threading.Thread(target=a1.save_money,args=(1000,))
#銀行卡取錢
t2 =threading.Thread(target=a1.pay_money,args=(500,))
t1.start()
t2.start()
t1.join()
t2.join()
print("余額:",a1.balance)
"""
打印結(jié)果:
開始存錢
存錢成功
開始取錢
取錢成功
余額: 1500
"""
練習(xí):寫一個服務(wù)器,可以不停的和多個客戶端聊天
server端:
import socket
from threading import Thread
class Receive(Thread):
def __init__(self,addr,conversation):
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.67",6666))
server.listen(100)
while True:
conversation,addr=server.accept()
#給服務(wù)器發(fā)送請求的客戶端連接創(chuàng)建一個子線程摧玫。在子線程中處理每個請求
r1 = Receive(addr,conversation)
r1.start()
client端:
import socket
client=socket.socket()
client.connect(("10.7.187.149",9001))
while True:
message =input("自己:")
client.send(message.encode("utf-8"))
練習(xí):下載所有的頭像圖片到本地
import requests
import json
import re
import threading
def download(url:str):
image_data=requests.get(url).content
file_name=url.split("/")[-1]
with open("images/"+file_name,"wb")as f:
f.write(image_data)
url1 ="https://www.apiopen.top/satinApi?type=1&page=1"
response =requests.get(url1)
json1 =response.json()
data =json1["data"]
for dict1 in data:
#拿到一個圖片地址耳奕,就為他創(chuàng)建一個線程對象,用來在子線程中下載這種圖片
t=threading.Thread(target=download,args=(dict1["profile_image"],))
# t.start()
# print("下載完成")
# with open("files/a.json","w",encoding="utf-8")as f:
# f.write(json.dumps(json1,ensure_ascii=False))
url1 ="https://www.apiopen.top/satinApi?type=1&page=1"
response =requests.get(url1)
text =response.text
re_str =r'"profile_image":(.+?),'
result =re.findall(re_str,text)
print(result)
# result =re.findall(re_str,json1)
# print(result)