目次
マルチタスク
プログラム内でマルチタスクをシミュレートする
マルチタスクの理解
マルチタスク用のスレッド
スレッド数を表示する
サブスレッドの実行と作成を確認する
Thread クラスを継承してスレッドを作成する
マルチスレッド共有グローバル変数(スレッド間通信)
マルチスレッド引数 -args
共有グローバル可変リソース競争
ミューテックス
デッドロック
デッドロックを回避する
キュースレッド
____________________________________________________________
マルチタスク
class sing():
for i in range(5):
print("我爱python")
print("我爱python---------运行结束")
def dence():
for i in range(5):
print("Pyhon很好玩")
print("python很好玩---------运行结束")
if __name__ == '__main__':
sing()
dence()
コードが上から下に順番に実行されることがわかりますが、コードの実行に遅延がないかどうかを考えたことはありますか? 遅延がある場合は、下方向に実行できるか、遅延したコードはコードはプッシュバックされます。もちろん、マルチスレッドがあります。以下にそれを紹介します。
マルチタスクについて理解する:
同時実行性と並列処理について聞いたことがある人もいるはずです。これら 2 つの言葉はスレッドでよく聞かれます。かわいい人は、コードが CPU 上で実行されることを知っています。CPU はシングルコアとマルチコアに分かれています。下の図を描いてみましょう 、
これらすべてのプロセスを実行したい場合、シングルコア CPU では不可能ですが、QQ が CPU 内に短時間滞在すると、WeChat の直後に起動して実行され、しばらく滞在してから終了します。そして、次のプロセスが入ってきて、外に出ると、これらのプロセスが同時に実行されていると誤って考えることができますが、実際には存在しません。これが並行性、並行性です。偽のマルチタスクです。 CPU が現在実行中のタスクより小さい
マルチコア CPU が実現可能であることがわかります。これは並列処理、並列処理です。実際のマルチタスク CPU は、現在実行中のタスクよりも大きいです。
マルチタスク用のスレッド
コードのデモは次のとおりです。
from threading import Thread
def sing():
#子线程
for i in range(5):
print("我爱python")
print("我爱python---------运行结束")
def dence():
#子线程
for i in range(5):
print("Pyhon很好玩")
print("python很好玩---------运行结束")
"""这是一个主线程"""
if __name__ == '__main__':
for i in range(2):
# 创建线程对象(这还不算是创建线程完成)
sin=Thread(target=sing)
# 启动(这里才算完成线程创建)
sin.start()
結果:
わからなくても大丈夫、一つずつ説明していきます。
まず、以前に紹介したスレッド化モジュールをダウンロードする必要があるため、ここではあまり紹介しません。
インポートスレッド
スレッド オブジェクトを作成します (スレッドは完全には作成されていません)。
サブスレッドの実行と作成を確認します。
from threading import Thread
import time
def sing():
#子线程
for i in range(5):
print("我爱python")
print("我爱python---------运行结束")
time.sleep(10)
def dence():
#子线程
for i in range(5):
print("Pyhon很好玩")
print("python很好玩---------运行结束")
"""这是一个主线程"""
if __name__ == '__main__':
a=time.time()
for i in range(2):
# 创建线程对象(这还不算是创建线程完成)
sin=Thread(target=sing)
# 启动(这里才算完成线程创建)
sin.start()
b=time.time()
print(b-a)
結果:
出力時間はメインプログラムの実行時間であることがわかります。サブスレッドの実行時間を見てみましょう
from threading import Thread
import threading
import time
def sing():
#子线程
for i in range(100):
print("我爱python")
print("我爱python---------运行结束")
time.sleep(2)
def dence():
#子线程
for i in range(100):
print("Pyhon很好玩")
print("python很好玩---------运行结束")
"""这是一个主线程"""
if __name__ == '__main__':
a=time.time()
lis=[]
for i in range(1):
# 创建线程对象(这还不算是创建线程完成)
t=threading.Thread(target=sing)
# 启动(这里才算完成线程创建)
t.start()
lis.append(t)
for i in lis:
i.join()
b = time.time()
print(b-a)
print("主程序运行到结尾")
結果:
join() は、メインプログラムの実行が開始される前に、子スレッドの実行が完了するのを待ちます。
Web ページ (通常バージョンとマルチスレッド バージョン) を一緒にクロールして、実行時間を確認してみましょう。
import requests
import threading
from lxml import etree
import time
def prase_url(url,header):
response=requests.get(url, headers=header)
return response
def parse_data(html):
e_html=etree.HTML(html)
new_html=e_html.xpath('//div[@id="htmlContent"]//text()')
# print("".join(new_html).strip())
h1=e_html.xpath('//div[@class="chapter-detail"]/h1/text()')[0]
print(h1)
return h1,"".join(new_html).strip()
def save_data(data):
with open("./小说/{}.txt".format(data[0]),"w",encoding="utf-8")as f:
f.write(data[1])
def main(urls):
"""主要的业务逻辑"""
# url
for url in urls:
header={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
}
# 发送请求获取响应
response=prase_url(url,header)
html=response.text
# print(html)
# 数据的提取
data=parse_data(html)
# 保存
save_data(data)
if __name__ == '__main__':
a=time.time()
lis=[]
urls=[]
for i in range(56, 93):
url = "http://www.quannovel.com/read/620/2467{}.html".format(i)
urls.append(url)
# for i in range(2):
# t1=threading.Thread(target=main,args=(urls,))
# t1.start()
# lis.append(t1)
# for t in lis:
# t.join()
# 单线程
main(urls)
b=time.time()
print(b-a)
print("主线程运行结束,等待子线程运行结束")
最初のマルチスレッド:
import requests
import threading
from lxml import etree
import time
def prase_url(url,header):
response=requests.get(url, headers=header)
return response
def parse_data(html):
e_html=etree.HTML(html)
new_html=e_html.xpath('//div[@id="htmlContent"]//text()')
# print("".join(new_html).strip())
h1=e_html.xpath('//div[@class="chapter-detail"]/h1/text()')[0]
print(h1)
return h1,"".join(new_html).strip()
def save_data(data):
with open("./小说/{}.txt".format(data[0]),"w",encoding="utf-8")as f:
f.write(data[1])
def main(urls):
"""主要的业务逻辑"""
# url
for url in urls:
header={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
}
# 发送请求获取响应
response=prase_url(url,header)
html=response.text
# print(html)
# 数据的提取
data=parse_data(html)
# 保存
save_data(data)
if __name__ == '__main__':
a=time.time()
lis=[]
urls=[]
for i in range(56, 93):
url = "http://www.quannovel.com/read/620/2467{}.html".format(i)
urls.append(url)
for i in range(2):
t1=threading.Thread(target=main,args=(urls,))
t1.start()
lis.append(t1)
for t in lis:
t.join()
# 单线程
# main(urls)
b=time.time()
print(b-a)
print("主线程运行结束,等待子线程运行结束")
import requests
import threading
from lxml import etree
import time
def prase_url(url,header):
response=requests.get(url, headers=header)
return response
def parse_data(html):
e_html=etree.HTML(html)
new_html=e_html.xpath('//div[@id="htmlContent"]//text()')
# print("".join(new_html).strip())
h1=e_html.xpath('//div[@class="chapter-detail"]/h1/text()')[0]
print(h1)
return h1,"".join(new_html).strip()
def save_data(data):
with open("./小说/{}.txt".format(data[0]),"w",encoding="utf-8")as f:
f.write(data[1])
def main(i):
"""主要的业务逻辑"""
# url
url="http://www.quannovel.com/read/620/2467{}.html".format(i)
header={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
}
# 发送请求获取响应
response=prase_url(url,header)
html=response.text
# print(html)
# 数据的提取
data=parse_data(html)
# 保存
save_data(data)
if __name__ == '__main__':
a=time.time()
lis=[]
for i in range(56,93):
t1=threading.Thread(target=main,args=(i,))
t1.start()
lis.append(t1)
for t in lis:
t.join()
# 单线程
# for i in range(56,93):
# main(i)
b=time.time()
print(b-a)
print("主线程运行结束,等待子线程运行结束")
結果
可愛い子たちはこれを見れば何が起こっているのか分かるでしょう、スレッドに時間がかかりすぎます、スレッドに問題があるため、一つずつ分析しましょう、
最初のマルチスレッド: 複数のスレッドが一緒に書き込みを行うため、スレッドの実行時間が CPU によって決定されるため、リソースの競合が発生します。
かわいい子たちが実行しているときに、5 つのスレッドの実行結果が正しくないことがわかります。その理由は何ですか? つまり、各スレッドは最初から最後まで実行されます。スレッドが作成されるたびに、パラメーターは介入すると、私たちが商品を購入するたびに、売り切れたら販売者が商品を補ってくれるようです, t1=threading.Thread(target=main, args =(urls,)) がそのような原則であるか、削除を設計するか、すべてを一度に実行します。
注意してみると、主にリソースの競合により、データの保存が乱れていることがわかります。
上図に示すように、最初のマルチスレッドはこのような状況になります。これを防ぎたい場合は、ここで各スレッドが実行されるようにロックを追加し、次のスレッドが書き込みできないようにする必要があります。後でロックを使用します。
2 番目のマルチスレッド:多数のスレッドを作成したことがわかりますが、その結果、作成した各スレッドは 1 つの Web サイトのみをクロールすることになり、コードの実行に目に見えない負担が追加されます。大事なことですが、私たちが作成するスレッドはすべて適切なものである必要があります。
Thread クラスを継承してスレッドを作成する
import requests
import threading
from lxml import etree
import time
import queue
# 这个类用于爬取数据
class My_Thread(threading.Thread):
def __init__(self,urls,header,datas):
super().__init__()
self.urls=urls
self.header=header
self.datas=datas
# print(self.urls.qsize())
def prase_url(self,url):
response=requests.get(url, headers=self.header)
return response
def parse_data(self,html):
e_html=etree.HTML(html)
new_html=e_html.xpath('//div[@id="htmlContent"]//text()')
# print("".join(new_html).strip())
h1=e_html.xpath('//div[@class="chapter-detail"]/h1/text()')[0]
# print(h1)
print("获取中")
return (h1,"".join(new_html).strip())
def run(self):
"""主要的业务逻辑"""
while not self.urls.empty():
# url
a=self.urls.get()
# 发送请求获取响应
response=self.prase_url(a)
html=response.text
# 数据的提取
data=self.parse_data(html)
self.datas.put(data)
# print(self.datas.qsize())
# 这个类用于保存文件
class Save_data(threading.Thread):
def __init__(self,datas):
super().__init__()
self.datas=datas
print(1)
def run(self):
while not self.datas.empty():
a=self.datas.get()
print("保存中")
with open("./小说/{}.txt".format(a[0]), "w", encoding="utf-8")as f:
f.write(a[1])
def main():
# url
urls = queue.Queue()
datas=queue.Queue()
for i in range(56, 93):
url = "http://www.quannovel.com/read/620/2467{}.html".format(i)
urls.put(url)
# print(urls.qsize())
header={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
}
# 创建多线程
lis=[]
for i in range(5):
my_thead=My_Thread(urls,header,datas)
my_thead.start()
lis.append(my_thead)
for i in lis:
i.join()
# print(datas.get())
for i in range(5):
sa_da=Save_data(datas)
sa_da.start()
if __name__ == '__main__':
a=time.time()
main()
b=time.time()
print(b-a)
print("主线程运行结束,等待子线程运行结束")
結果:
マルチスレッド操作を見ると、一度にクロールできることがわかります。次に、コードを分析してみましょう。
1. Threa クラスを作成するときは、親クラス threading.Thread を継承する必要があります。
2. その中には run(self) というメソッドがあります!!! これは書き換えることができますが、名前は変更できません。実行する必要があるコードを書くことは、マルチを学習していないときの main() と同等です。ねじ切り
3. 内部の mian() 関数は主にスレッドの作成に使用されます。
4.
lis の i の場合: i.join()
これを記述するには、クロールされたすべてのデータがクロールダウンするまで待機し、後でマルチスレッドをファイルに保存するときにデータが保存されないようにする必要があります。
5.queue.Queue(): キューを作成します。
マルチスレッド共有グローバル変数(スレッド間通信)
簡単に言うと、データ送信を完了するための媒体としてグローバル変数を使用します。
次に、グローバル変数を変更するには、global を追加する必要があるかという問題に遭遇します。
import threading
# 修改全局变量是否要加global(根据修改值是否发生地址的改变,地址改变就要加global)
num=0
#写
def task1(nu,n):
global num
num+=nu
print("task1=",num)
print("n=%d"%n)
#读
def task2():
print("task2=",num)
def main():
# 创建子线程
t1=threading.Thread(target=task1,args=(3,4))
t2=threading.Thread(target=task2)
# 启动子线程(这里才是是完全创建子线程)
t1.start()
t2.start()
print("main.....",num)
if __name__ == '__main__':
main()
結果:
関数にグローバルを追加するかどうかは、値を変更することでアドレスが変更されるかどうかによって異なります。
ミューテックス
リソース競合を解決するために使用されます、いわゆるリソース競合とは、複数のスレッドが同じ方向に動作することを意味します。
別の簡単なコードを次に示します。
ロックなし:
import threading
import time
"""两个同时写入,不加锁"""
num=0
def task1():
global num
for i in range(100000000):
num+=1
print("task1.......%d"%num)
def task2():
global num
for i in range(100000000):
num+=1
print("task2.......%d"%num)
def main():
# 创建子线程
t1=threading.Thread(target=task1)
t2=threading.Thread(target=task2)
# 启动
t1.start()
t2.start()
print("main....%d"%num)
if __name__ == '__main__':
main()
結果:
コード:
RLock() を使用して複数のロックを作成する
import threading
import time
"""加锁"""
num=0
# 创建一个锁
# mutex=threading.Lock()
mutex=threading.RLock()
def task1():
global num
# 锁定(保证数据能正常存储)
mutex.acquire()
mutex.acquire()
for i in range(100000000):
num+=1
mutex.release()
mutex.release()
# 解锁(使下一个线程能使用)
print("task1.......%d"%num)
def task2():
global num
# 锁定(保证数据能正常存储)
mutex.acquire()
mutex.acquire()
for i in range(100000000):
num+=1
mutex.release()
mutex.release()
# 解锁(使下一个线程能使用)
print("task2.......%d"%num)
def main():
# 创建子线程
t1=threading.Thread(target=task1)
t2=threading.Thread(target=task2)
# 启动
t1.start()
t2.start()
print("main....%d"%num)
if __name__ == '__main__':
main()
結果:
Lock を使用してロックを作成する
import threading
import time
""加锁"""
num=0
# 创建一个锁
mutex=threading.Lock()
# mutex=threading.RLock()
def task1():
global num
# 锁定(保证数据能正常存储)
# mutex.acquire()
mutex.acquire()
for i in range(100000000):
num+=1
mutex.release()
# mutex.release()
# 解锁(使下一个线程能使用)
print("task1.......%d"%num)
def task2():
global num
# 锁定(保证数据能正常存储)
mutex.acquire()
# mutex.acquire()
for i in range(100000000):
num+=1
mutex.release()
# mutex.release()
# 解锁(使下一个线程能使用)
print("task2.......%d"%num)
def main():
# 创建子线程
t1=threading.Thread(target=task1)
t2=threading.Thread(target=task2)
# 启动
t1.start()
t2.start()
print("main....%d"%num)
if __name__ == '__main__':
main()
結果:
コード:
import threading
import time
mutex=threading.Lock()
def task1():
global num
with mutex:
for i in range(100000000):
num+=1
print("task1.......%d"%num)
def task2():
global num
with mutex:
for i in range(100000000):
num+=1
print("task2.......%d"%num)
def main():
# 创建子线程
t1=threading.Thread(target=task1)
t2=threading.Thread(target=task2)
# 启动
t1.start()
t2.start()
print("main....%d"%num)
if __name__ == '__main__':
main()
結果;
threading.Lock()、作成できるロックは 1 つだけ、ロックを解除できるのは 1 つだけです
threading.RLock()、複数のロックの作成とロック解除のみ可能
取得() ロック 解放() ロック解除
ミューテックスあり: 自動的にロックおよびロック解除されます。オープンの場合と同じ効果があります。
キュースレッド
import queue
import threading
# 创建队列
# a=queue.Queue(5)
# for i in range(5):
# a.put(i) #存入元素
# print(a.full())
# print(a)
# for i in range(5):
# # print(a.get())
# print(a.get_nowait())
# print(a.empty())
# 创建队列
q=queue.Queue()
num = 0
q.put(num)#把num的值存入
def task1():
for i in range(10000000):
num=q.get() # 创建一个名为num的局部变量
num+=1
q.put(num)
# return q # 反不返回没事
def task2():
for i in range(1000000):
num = q.get() # 创建一个名为num的局部变量
num += 1
q.put(num)
# return q
def main():
# 创建子线程
t1=threading.Thread(target=task1)
t2=threading.Thread(target=task2)
# 启动
t1.start()
t2.start()
t1.join()
t2.join()
print("main....%d"%num)
if __name__ == '__main__':
main()
要約する
一般に、スレッドの目的は、時間の利用を大幅に改善し、コンピュータ操作の効率を向上させ、スレッドがないとクロールが多すぎるため、実行が非常に遅くなるのを防ぐことです。