05言う:多くのスレッド爬虫類のpython

Pythonの爬虫類多くのスレッド

1.はじめに

私たちは、コンピュータに、私たちは、このような、Webブラウジングなど、音楽、タイピング、を聞きながらと、同時にソフトウェアの多くを開くことができることを知って、それは非常に正常なようです。しかし、それについて考える、なぜこれほど多くのコンピュータ・ソフトウェアは、それを実行することができますか?これは、コンピュータに2つの重要な概念が含まれます。マルチプロセス及びマルチスレッド

同様に、クローラの準備で、効率を向上させるためにクロール、我々は複数のタスクの爬虫類を実行したい場合があります。ここでは、あまりにも、マルチプロセスとマルチスレッドの知識を必要とする必要があります。

この講演では、まず見ていきますマルチスレッド基本原理、そしてどのようにPythonで複数のスレッドを実装します。

2.マルチスレッドの意味

マルチスレッドといえば、あなたはスレッドが何であるかを開始する必要があります。しかし、私はスレッドであるかを把握したい、彼らはプロセスであり、どのように開始する必要があります。

私たちのプロセスは、缶として理解することができますプログラムは、独立したユニットを実行しますここで、例:

このようなブラウザを開き、ブラウザを開くなどプロセス;テキストエディタを開き、テキストエディタを開きます。プロセスしかし同時に多くのものを扱うことができるプロセスは、ブラウザのように、我々はいくつかのページを演奏し、複数のタブで複数のページ、音楽では一部のページ、ビデオでいくつかのページを開くことができますアニメーションは、彼らがお互いを乱すことなく同時に実行することができます。なぜそれを実行するために、両方同時に非常に多くのタスクを達成することができますか?ここでは、必要なリードですスレッドコンセプトは、実際には、すべてのタスクということは、実際に実行のスレッドに対応しています。

プロセス?これは、スレッドのコレクションですプロセスは、1つまたは複数のスレッドで構成され、スレッドはオペレーティングシステムの動作のスケジューリングの最小単位は、プロセス内の最小単位操作です。たとえば、スレッドプレイ音楽である上記のブラウザプロセスは、プレイ映像がスレッドがあり、当然のことながら、そこに同時に実行するために多くの他のスレッド、これらのスレッドの同時または並列実行され、最終的には全体のブラウザを同時に実行することができます非常に多くの作業。

スレッドの概念を理解し、非常に簡単に理解するために、マルチスレッド、マルチスレッドは、同時に複数のスレッドを実行する処理であります、ブラウザのシーン前にも言ったが、典型的なマルチスレッド実行です。

3.同時とパラレル

それはマルチプロセスに来るとマルチスレッドの場合は、ここでは、あること、二つの概念を説明する必要がありますによって複雑パラレル私たちは、コンピュータで実行されるプログラムは、基本的なプロセッサ命令がストリップを実行することにより実現していることを知っています。

①同時、と呼ばれる英語(同時性)

これは、同じ時間を指し唯一の命令の実行がある場合もあります、しかし、命令スレッドの対応する複数迅速に実行回転されます。例えば、プロセッサ、スレッドAは、次にBスレッドその命令の実行の期間は、命令期間を実行し、その後、実行時間をスレッドに切り替えます。

命令を実行し、非常に、非常に速い速度を切り替えるための高速プロセッサので、知覚し、コンピュータを持っていない人は、スレッド切り替え操作、複数のは、それが複数のスレッドを同時に実行肉眼で見えるようにされ、このプロセスのコンテキストで実行しました。これは連続的に複数のスレッドと実行を切り替えるだけマイクロプロセッサであり、実行の各スレッドは、実行中の唯一のスレッドが存在する、プロセッサを時間セグメント、同じ時間を取るであろう。

平行②、英語は(パラレル)と呼ばれます

これは、同じ時間に複数の命令内を指し同時に複数のプロセッサ上で実行、これは、並列プロセッサの複数に依存しなければなりません。マクロとミクロレベルの両方から、複数のスレッドが同時に一緒に実行しています。

パラレルは、当社のコンピュータが唯一つのプロセッサコアを持っている場合、並列処理を実現することは不可能である、マルチプロセッサシステム内に存在することができます。単独コアので、シングルプロセッサおよびマルチプロセッサシステム内のすべてのBEの存在によって複雑に、同時に達成することができます。

例えば、そのようなシステムは、同時に複数のスレッドを実行するプロセッサを必要とします。お使いのシステムは、唯一つのプロセッサコアを持っており、それが唯一の方法によって並行スレッドを実行することができます。システムは、複数のプロセッサコアを有する場合に実行されるときに、2つの並列実行スレッドを達成するように、一つのコアのスレッドが、別のスレッドが、別のコアを実行することができる場合、他のスレッドは、もちろん、別のスレッドでもよいです同じ実行コア上で、それらの間の同時実行があります。予定のオペレーティングシステムに応じて、実装の具体的な様式、。

4.マルチスレッドのアプリケーションシナリオ

プログラムの過程では、いくつかの操作があり、このような結果ページの応答を待って、データベースクエリを返すの結果を待っているとして、より多くの時間がかかるか、待つこと、です。あなたはシングルスレッドプロセッサを使用している場合、これらの操作の完了は、他の操作を下に続けて、このスレッドは、プロセスを待った後、プロセッサは他の明らかに操作を行うことが可能になるまで待たなければなりません。あなたが複数のスレッドを使用する場合は、スレッドが他のスレッドを実行するために待機しているとき、プロセッサは、このように全体としての効率を向上させ、ことができます。

上記のシナリオのように、多くの場合、実装プロセス内のスレッドは待つことです。例えば、ウェブクローラーは、サーバーへの要求を開始した後爬虫類、返さなければなりませんサーバーからの応答を待つ時間の期間がある非常に典型的な例でありますこのタスクは、IO集約型のタスクに属しこの作業のために、我々はマルチスレッドプロセッサを有効にした場合、それによって、クロールの全体的な効率を高める他のタスクに対処するために待っているのスレッドで処理することができます。

しかし、すべてのタスクがIO集約型のタスクです、と呼ばれるタスクがあります計算集約型のタスクまた、CPU集約型のタスクを呼び出すことができます。名前が示すように、それは、タスクを実行することであるプロセッサに参加することが求められています。我々はマルチスレッド開いた場合は、この時点では、スイッチに1つの計算集約型のタスクからプロセッサの切り替え、別のアップの計算集約型のタスクに、プロセッサはまだ、常に忙しいコンピューティングを停止することはありません、これは全体的には保存されませんコンピューティングタスクの合計量は、処理するための時間は、一定です。あまりにも多くのスレッドの数は、だけでなく、マルチスレッド切り替えの過程でいくつかの時間を過ごすことになります場合は、全体の効率が低くなります。

タスクはすべて計算集約的なタスクではないのであれば、私たちは、プログラムの実行の全体的な効率を改善するために、複数のスレッドを使用することができます。特に、AウェブクローラIO集中型のタスクのために、複数のスレッドの使用が大幅にクロール手順の全体的な効率を改善します。

5.Pythonマルチスレッド

Pythonでは、マルチスレッドモジュールの実装は、スレッドと呼ばれていますこれは、Pythonに付属しているモジュールです。ここでは、理解するために、マルチスレッド達成するための方法の下にスレッドを使用します。(私たちは具体的な実践的なコースで説明し、うまくを理解します)

直接の子スレッドを作成①Thread

まず、我々はスレッドを作成するために、Threadクラスを使用することができ、あなたは== ==ターゲットパラメータのメソッド名を作成するときに指定する必要が呼び出されるメソッドは、追加のパラメータが渡さ必要とする場合、実行されている、ことができます==引数パラメータスレッドの= =指定します。例としては、次のとおりです:

import threading, time

def target(second):
    print(f'Threading {threading.current_thread().name} is runing')
    print(f'Threading {threading.current_thread().name} sleep {second}s')
    time.sleep(second)
    print(f'Threading {threading.current_thread().name} ended')

print(f'Threading {threading.current_thread().name} is runing')

for i in [1, 5]:
    t = threading.Thread(target=target, args=[i])
    # t = threading.Thread(target=target, args=(i,))
    t.start()
print(f'Threading {threading.current_thread().name} is ended')


# 输出
Threading MainThread is runing
Threading Thread-1 is runing
Threading Thread-1 sleep 1s
Threading Thread-2 is runing
Threading Thread-2 sleep 5s
Threading MainThread is ended
Threading Thread-1 ended
Threading Thread-2 ended

ここで最初のターゲットと呼ばれるメソッドを宣言し、それは第二のパラメータを受け取り、実装はこの方法で見つけることができ、実際にtime.sleepスリープ操作を実行すると、2番目のパラメータは前と後の両方、スリープ状態にする秒数ですそれはメインスレッドであれば、子スレッドは、その値がスレッド*であれば、コンテンツの一部、我々はthreading.current_thread(して、スレッドから抜け出すために持っているの名前)を印刷。名前、その値は、MainThreadです。

その後、我々はTHEAD二つのスレッドによってクラスを作成し、ターゲット・パラメータ名は、我々が定義した方法であり、引数はリストの形で渡されました。私は、それぞれ、1と5である2つのサイクルは、そのステートメントが完了した後、2つのスレッドが休眠1と5秒であることを、我々はスレッドを実行するために、startメソッドを呼ぶようになりました。

観察は、私たちは、主に2つのスレッドと子スレッドMainThreadスレッド1、スレッド2である3つのスレッドの合計が生成されていることがわかります。加えて、我々は、メインスレッドの端部が最初に実行することが観察され、ランの終わりまで連続してスレッド1、スレッド2に従った、それぞれ、一秒と4秒の間隔。メインスレッドが子スレッドを待たないことをこのショーは、実行の終了前に実行を終了しますが、直接、少し常識を引き抜きました。

おそらく、我々は、誰もが見ることのために、私はさらにいくつかのパラメータを追加することになり、あまりにも明確なこのケースを見ていません

import threading, time

def target(second):
    print(f'Threading {threading.current_thread().name} is runing')
    print(f'Threading {threading.current_thread().name} sleep {second}s')
    time.sleep(second)
    print(f'Threading {threading.current_thread().name} ended')

print(f'Threading {threading.current_thread().name} is runing') # --> 1.这个就是主线程了

for i in [1,3,5]:
    t = threading.Thread(target=target, args=[i])
    # t = threading.Thread(target=target, args=(i,))
    t.start()

print(f'Threading {threading.current_thread().name} is ended')# --> 2.在创建完子线程后,主线程就结束了,

スクリーンショットの終わりに:
ここに画像を挿入説明

私たちはただ、あなたは(私はそれがブロックされている理解して)それぞれの子スレッドオブジェクトは、メソッドに参加呼び出さ完成実行させることができた子スレッドのためにメインスレッドが待機した後に終了したい場合は、以下を達成するために:

for i in [1, 5]:
    t = threading.Thread(target=target, args=[i])
    t.start()
    t.join()
# 输出
Threading MainThread is runing
Threading Thread-1 is runing
Threading Thread-1 sleep 1s
Threading Thread-1 ended
Threading Thread-2 is runing
Threading Thread-2 sleep 5s
Threading Thread-2 ended
Threading MainThread is ended

このように、メインスレッドが終了するの子スレッドを待つ必要があり、メインスレッドが実行して終了するまで継続されました。

②相続Threadクラスは、子スレッドを作成します

また、我々はまた、継承Threadクラスの方式を通じてスレッドを作成することができ、内側に書かれて実行されるこのスレッドのメソッドは、クラスメソッドを実行することができます。上記の例同等のリライト

import threading, time
class MyThread(threading.Thread):
    def __init__(self, second):
        threading.Thread.__init__(self)
        self.second = second
    def run(self):
        print(f'Threading {threading.current_thread().name} is runing')
        print(f'Threading {threading.current_thread().name} sleep {self.second}s')
        time.sleep(self.second)
        print(f'Threading {threading.current_thread().name} is ended')


print(f'Threading {threading.current_thread().name} is runing')

for i in [1, 5]:
    t = MyThread(i)
    t.start()
    t.join()
print(f'Threading {threading.current_thread().name} is ended')

# 输出
Threading MainThread is runing
Threading Thread-1 is runing
Threading Thread-1 sleep 1s
Threading Thread-1 is ended
Threading Thread-2 is runing
Threading Thread-2 sleep 5s
Threading Thread-2 is ended
Threading MainThread is ended

これは、効果は同じである実行する2つの実装を、見ることができます。

③デーモンスレッド

スレッド内のデーモンスレッドと呼ばれる概念がありますスレッドがデーモンスレッドとして設定されている場合、それはこのスレッドが==「重要でない」であることを意味し、どのメインスレッドが終わり、デーモンは、スレッドが終了した実行されていない場合、それは最後に強制されることを意味します。Pythonで我々はできます setdaemon ==デーモンスレッドとしてスレッドを設定する方法。

例としては、次のとおりです:

import threading, time

def target(second):
    print(f'Threading {threading.current_thread().name} is runing')
    print(f'Threading {threading.current_thread().name} sleep {second}s')
    time.sleep(second)
    print(f'Threading {threading.current_thread().name} is ended')

print(f'Threading {threading.current_thread().name} is runing')
t1 = threading.Thread(target=target, args=[2])
t1.start()
t2 = threading.Thread(target=target, args=[5])
t2.setDaemon(True) # --> 就是这个啦
t2.start()
print(f'Threading {threading.current_thread().name} is ended')

ここがT2なり、デーモンスレッドを設定するための方法では、setdaemon(明らかに彼は睡眠5秒に持っている)ので、メインスレッドその際、完成ランニング、糸端の端とt2のスレッド。

結果:

Threading MainThread is runing
Threading Thread-1 is runing
Threading Thread-1 sleep 2s
Threading Thread-2 is runing
Threading Thread-2 sleep 5s
Threading MainThread is ended
Threading Thread-1 is ended

我々は、スレッド2印刷終了のニュースを参照してくださいスレッド2メインスレッドからの出口とし、終了していない、見ることができます。

しかし、注意してください、あなたはそれがデーモンスレッドであろうと、終了した後、我々はT1せて、結合方法を呼び出しているT2、メインスレッドがまだそれぞれの子スレッドが終了するのを待ちますとあれば、参加して、メソッドへの一切の呼び出しが存在しないことがあります。

6.ミューテックス(スレッド安全でないという問題を解決します)

複数のこのようなプロセスのように、共有リソースのプロセス内のスレッド、カウントするグローバル変数の数があり、そして今、我々は複数のスレッドを宣言し、プラス1つのカウントは、各スレッドが実行されている与え、見てみましょうどのように効果が次のように、コードが実装されています。

import threading, time

count = 0

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global count
        temp = count + 1
        time.sleep(0.001)
        count = temp
threads = []
for _ in range(1000):
    thread = MyThread()
    thread.start()
    threads.append(thread)
for thread in threads:
    thread.join()
print(f'Final count: {count}')

ここで、我々は1000個のスレッドを宣言し、各スレッドは現在、グローバル変数の現在のカウント値に取って、その後、休眠短時間し、新しい価値を与えることを数えます。

その方法は、常識によると、最終カウント値を1000にする必要があります。しかし、それは真実ではない、のは、それを実行して見てみましょう。

結果は以下の通りであります:

Final count: 57

最終的な結果は、実際には57ですが、複数の実行や業績環境の変化が異なっています。

これはなぜでしょうか?カウント値が共有されているため、各スレッドは、異なるスレッドで結果、コード行が、これらのスレッドを同時にまたはいくつかのスレッドを並列に実行することができるときにカウント= TEMPカウントの現在値を取得するために実行することができます同じことが、いくつかのスレッド数が1だけインクリメントされ、操作が小さすぎて、最終的な結果につながる、力ではないが有力、カウント値を取得するかもしれません。

データのため、同時に複数のスレッドが読み取りまたは修正するのであれば、予期しない結果が表示されます。これを避けるために、我々は、我々がthreading.Lockを使用する必要があり、操作するためのデータ保護の必要性をロックすることができ、同期を達成するために、複数のスレッドを同期する必要があります。

ロック保護、それは何を意味するのでしょうか?データの前にスレッドは、あなたが解放されるように、他のスレッドがロックされた後に発見されたのでことを、我々は、ダウンし続けることができない、常にロックを待機し、ロックする必要がある、唯一のロックねじロック放し、他のスレッドが終了したロックデータとメイクの変更、修正に進み、その後、ロックを解除することができます。同時にデータを操作するだけで一つのスレッドこの保証し、複数のスレッドが同時に読み込まれませんし、最終的な結果は、実行のペアであるので、同じデータを変更します。

私たちは、次のように読み込むためのコードを変更することができます。

import threading, time

count = 0

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global count
        lock.acquire() # 计算前先加个锁
        temp = count + 1
        time.sleep(0.001)
        count = temp
        lock.release() # 计算完后解锁

lock = threading.Lock()
threads = []
for _ in range(1000):
    thread = MyThread()
    thread.start()
    threads.append(thread)
for thread in threads:
    thread.join()
print(f'Final count: {count}')

ここでは、実際には、例えばthreading.Lockで、その後、runメソッドの内部で、最初のロックの前にカウントを取得、あなたは複数のスレッドが同時にアクセスしないようにすることを、ロックの解除後に再度カウント数を変更し、変更し、ロックオブジェクトが宣言しますcountの値。

結果は以下の通りであります:

Final count: 1000

このような操作は、通常の状態になります。

マルチスレッドのPythonの内容について、などセマフォ、キュー、として、より多くの使用をtheadingについては、公式ドキュメントを参照することができ、当分の間、ここではそれらを紹介:https://docs.python.org/zh-cn/3.7/library /threading.html#module-threading。

マルチスレッドの問題7.Python

PythonのGILの制限のためには、シングルコアまたはマルチコアの条件のいずれかで、その結果、同時にあなただけのマルチコア並列を活用Pythonはできないマルチスレッドで、その結果、一つのスレッドを実行することができます。

GILは、グローバルインタプリタロック、もともとデータのセキュリティと配慮のために設計されたグローバルインタプリタロック、中国の翻訳と呼ばれます。

次のようにPythonの複数のスレッドでは、各スレッドの実装です。

STEP1:GET GIL
STEP2:対応するスレッド化コードの実装
STEP3:GILリリース

目に見える、スレッドが実行したい、あなたが最初にGILを取得する必要があり、我々はGILパスとして見ることができる、とPythonのプロセスでは、GIL一つだけ。スレッドのパスを取得し、実装が許可されていません。これはさえ、マルチコアの条件の下で、Pythonのプロセスで複数のスレッドが、同じ時間だけつのスレッドを実行できるためにつながります。

しかし、IO集約型のタスクなど爬虫類のために、この問題はあまりにも素晴らしいではありません。全体に比べためGIL、マルチスレッド動作効率の計算集約型のタスクのために実際にシングルスレッドよりも低くてもよいです。

爬虫類の教科書の教材は、あなたに感謝し、CUI清のコンテンツの大半は教師だったと私はこれらの小さなのいくつかを理解して、間違った場所下さい接触、私に何かがある場合は、このシリーズは、クラスの後、私のノートであると言います!

公開された12元の記事 ウォン称賛7 ビュー165

おすすめ

転載: blog.csdn.net/caiyongxin_001/article/details/104888210