1.耗時(shí)操作
一個(gè)線程默認(rèn)有一個(gè)線程,這個(gè)線程叫主線程,默認(rèn)情況下所有的任務(wù)(代碼)都是在主線程中執(zhí)行的
import time,datetime
def download(film_name):
print("開(kāi)始下載%s"%film_name,datetime.datetime.now())
time.sleep(5)#程序執(zhí)行到這個(gè)地方,線程會(huì)阻塞(停5s),再執(zhí)行后面的代碼
print("%s下載結(jié)束"%film_name,datetime.datetime.now())
if __name__ == '__main__':
download("喜洋洋與灰太狼")
download("花園寶寶")
2.多線程
python中提供了threading模塊
默認(rèn)創(chuàng)建的線程叫主線程,其他的線程叫子線程。如果希望代碼在子線程中執(zhí)行,必須手動(dòng)創(chuàng)建線程對(duì)象。
import threading
import time,datetime
def download(film_name):
print("開(kāi)始下載%s"%film_name,datetime.datetime.now())
time.sleep(5)#程序執(zhí)行到這個(gè)地方,線程會(huì)阻塞(停5s),再執(zhí)行后面的代碼
print("%s下載結(jié)束"%film_name,datetime.datetime.now())
print(threading.current_thread())
if __name__ == '__main__':
# 1.創(chuàng)建線程對(duì)象
"""
Thread - 線程類(lèi)
Thread(target=函數(shù)名,args=參數(shù)列表) - 直接創(chuàng)建線程對(duì)象 - 返回線程對(duì)象
函數(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對(duì)應(yīng)的線程中調(diào)用download函數(shù),并且傳遞參數(shù)“海綿寶寶”
"""
t1.start()
t2.start()
#打印結(jié)果
"""
開(kāi)始下載海綿寶寶 2018-11-29 19:16:21.574319
開(kāi)始下載花園寶寶 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.線程類(lèi)的子類(lèi)
創(chuàng)建子線程除了直接創(chuàng)建Thread的對(duì)象,還可以創(chuàng)建這個(gè)類(lèi)的子類(lèi)對(duì)象
注意:一個(gè)進(jìn)程中有多個(gè)線程,進(jìn)程會(huì)在所有的線程結(jié)束才會(huì)結(jié)束;
線程中的任務(wù)執(zhí)行完了線程就結(jié)束了
from threading import Thread,current_thread
#1.聲明一個(gè)類(lèi)繼承Thread
class DownloadThread(Thread):
def __init__(self,file_name):
super().__init__()
self.file_name =file_name
#2.重寫(xiě)run方法
def run(self):
#這個(gè)方法中的代碼會(huì)在子線程當(dāng)中執(zhí)行
print("開(kāi)始下載:%s"%self.file_name)
print(current_thread())
# 3.創(chuàng)建線程對(duì)象
d1=DownloadThread("滴滴滴")
d1.start()
d2 =DownloadThread("啊啊啊")
d2.start()
# d1.run()#直接調(diào)用run方法,會(huì)在主線程中執(zhí)行
"""
打印結(jié)果:
開(kāi)始下載:滴滴滴
<DownloadThread(Thread-1, started 6960)>
開(kāi)始下載:啊啊啊
<DownloadThread(Thread-2, started 6432)>
"""
4.join
線程對(duì)象.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("開(kāi)始下載%s"%self.file_name)
a=random.randint(5,12)
time.sleep(a)
print("%s下載結(jié)束"%self.file_name,"耗時(shí)%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é)果:
開(kāi)始下載花園寶寶
開(kāi)始下載海綿寶寶
花園寶寶下載結(jié)束 耗時(shí)10秒
海綿寶寶下載結(jié)束 耗時(shí)11秒
耗時(shí): 11.00162935256958
"""
5.數(shù)據(jù)共享
注意:當(dāng)多個(gè)線程同時(shí)對(duì)同一個(gè)數(shù)據(jù)操作的時(shí)候,可能會(huì)出現(xiàn)數(shù)據(jù)混亂
多個(gè)線程對(duì)一個(gè)數(shù)據(jù)進(jìn)行操作,一個(gè)線程將一個(gè)數(shù)據(jù)讀出來(lái),還沒(méi)來(lái)得及存進(jìn)去,
另一個(gè)線程又去讀了,這個(gè)時(shí)候就可能產(chǎn)生數(shù)據(jù)安全隱患
解決方案:加鎖
Thread - 線程類(lèi)(創(chuàng)建子線程)
Lock - 鎖類(lèi) (創(chuàng)建鎖對(duì)象)
import time,threading
class Account(object):
def __init__(self,balance,name):
self.balance=balance
self.name =name
self.lock =threading.Lock()#創(chuàng)建鎖對(duì)象
def save_money(self,num):
"""存錢(qián)"""
self.lock.acquire()#加鎖
print("開(kāi)始存錢(qián)")
old_balance=self.balance
time.sleep(5)
self.balance=old_balance+num
print("存錢(qián)成功")
self.lock.release()#解鎖
def pay_money(self,num):
"""取錢(qián)"""
self.lock.acquire() # 加鎖
print("開(kāi)始取錢(qián)")
old_balance = self.balance
time.sleep(5)
self.balance = old_balance - num
print("取錢(qián)成功")
self.lock.release() # 解鎖
a1 =Account(1000,"小明")
#支付寶存錢(qián)
t1 =threading.Thread(target=a1.save_money,args=(1000,))
#銀行卡取錢(qián)
t2 =threading.Thread(target=a1.pay_money,args=(500,))
t1.start()
t2.start()
t1.join()
t2.join()
print("余額:",a1.balance)
"""
打印結(jié)果:
開(kāi)始存錢(qián)
存錢(qián)成功
開(kāi)始取錢(qián)
取錢(qián)成功
余額: 1500
"""
練習(xí):寫(xiě)一個(gè)服務(wù)器,可以不停的和多個(gè)客戶端聊天
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ā)送請(qǐng)求的客戶端連接創(chuàng)建一個(gè)子線程。在子線程中處理每個(gè)請(qǐ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:
#拿到一個(gè)圖片地址,就為他創(chuàng)建一個(gè)線程對(duì)象,用來(lái)在子線程中下載這種圖片
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)