3

マルチスレッド プログラミングについて聞くと、自分のプログラムを高速化する機会について考えますが、そうではありませんか?

import eventlet
from eventlet.green import socket
from iptools import IpRangeList


class Scanner(object):
    def __init__(self, ip_range, port_range, workers_num):
        self.workers_num = workers_num or 1000
        self.ip_range = self._get_ip_range(ip_range)
        self.port_range = self._get_port_range(port_range)
        self.scaned_range = self._get_scaned_range()

    def _get_ip_range(self, ip_range):
        return [ip for ip in IpRangeList(ip_range)]

    def _get_port_range(self, port_range):
        return [r for r in range(*port_range)]

    def _get_scaned_range(self):
        for ip in self.ip_range:
            for port in self.port_range:
                yield (ip, port)

    def scan(self, address):
        try:
            return bool(socket.create_connection(address))
        except:
            return False

    def run(self):
        pool = eventlet.GreenPool(self.workers_num)
        for status in pool.imap(self.scan, self.scaned_range):
            if status:
                yield True

    def run_std(self):
        for status in map(self.scan, self.scaned_range):
            if status:
                yield True


if __name__ == '__main__':
    s = Scanner(('127.0.0.1'), (1, 65000), 100000)
    import time
    now = time.time()
    open_ports = [i for i in s.run()]
    print 'Eventlet time: %s (sec) open: %s' % (now - time.time(),
                                                len(open_ports))
    del s
    s = Scanner(('127.0.0.1'), (1, 65000), 100000)
    now = time.time()
    open_ports = [i for i in s.run()]
    print 'CPython time: %s (sec) open: %s' % (now - time.time(),
                                                len(open_ports))

結果:

Eventlet time: -4.40343403816 (sec) open: 2
CPython time: -4.48356699944 (sec) open: 2

そして私の質問は、このコードをラップトップではなくサーバーで実行し、より多くのワーカーの値を設定すると、CPython のバージョンよりも速く実行されるのでしょうか? スレッドの利点は何ですか?

追加: そして、元のcpythonのスレッドを使用してアプリを書き直しました

import socket
from threading import Thread
from Queue import Queue

from iptools import IpRangeList

class Scanner(object):
    def __init__(self, ip_range, port_range, workers_num):
        self.workers_num = workers_num or 1000
        self.ip_range = self._get_ip_range(ip_range)
        self.port_range = self._get_port_range(port_range)
        self.scaned_range = [i for i in self._get_scaned_range()]

    def _get_ip_range(self, ip_range):
        return [ip for ip in IpRangeList(ip_range)]

    def _get_port_range(self, port_range):
        return [r for r in range(*port_range)]

    def _get_scaned_range(self):
        for ip in self.ip_range:
            for port in self.port_range:
                yield (ip, port)

    def scan(self, q):
        while True:
            try:
                r = bool(socket.create_conection(q.get()))
            except Exception:
                r = False
            q.task_done()

    def run(self):
        queue = Queue()
        for address in self.scaned_range:
                queue.put(address)
        for i in range(self.workers_num):
                worker = Thread(target=self.scan,args=(queue,))
                worker.setDaemon(True)
                worker.start()
        queue.join()


if __name__ == '__main__':
    s = Scanner(('127.0.0.1'), (1, 65000), 5)
    import time
    now = time.time()
    s.run()
    print time.time() - now

結果は

 Cpython's thread: 1.4 sec

そして、これは非常に良い結果だと思います。標準の nmap スキャン時間として次のように考えています。

$ nmap 127.0.0.1 -p1-65000

Starting Nmap 5.21 ( http://nmap.org ) at 2012-10-22 18:43 MSK
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00021s latency).
Not shown: 64986 closed ports
PORT      STATE SERVICE
53/tcp    open  domain
80/tcp    open  http
443/tcp   open  https
631/tcp   open  ipp
3306/tcp  open  mysql
6379/tcp  open  unknown
8000/tcp  open  http-alt
8020/tcp  open  unknown
8888/tcp  open  sun-answerbook
9980/tcp  open  unknown
27017/tcp open  unknown
27634/tcp open  unknown
28017/tcp open  unknown
39900/tcp open  unknown

Nmap done: 1 IP address (1 host up) scanned in 0.85 seconds

そして私の質問は今です:これはスレッドではなくEventletにとって特別なものであり、なぜタスクを高速化しないのかを理解できるように、Eventletにどのように実装されているのですか?

Eventlet は、OpenStack などの主要なプロジェクトの多くで使用されています。しかし、なぜでしょうか? 非同期でDBに大量のクエリを実行するだけですか?

4

6 に答える 6

6

Cpython スレッド:

  • 各 cpython スレッドは、OS レベルのスレッド (ユーザー空間の軽量プロセス/pthread) にマップされます。

  • Python コードを同時に実行する cpython スレッドが多数ある場合: グローバル インタープリター ロックにより、一度に 1 つの cpython スレッドのみが python を解釈できます。残りのスレッドは、Python 命令を解釈する必要があるときに GIL でブロックされます。多くの Python スレッドがある場合、これにより処理が大幅に遅くなります。

  • ここで、Python コードがほとんどの時間をネットワーク操作 (送信、接続など) 内で費やしている場合: この場合、GIL がコードを解釈するために戦うスレッドが少なくなります。したがって、GIL の効果はそれほど悪くありません。

イベントレット/グリーン スレッド:

  • 上記から、cpython にはスレッドに関するパフォーマンスの制限があることがわかります。Eventlets は、単一のコアで実行される単一のスレッドを使用し、すべてにノンブロッキング I/O を使用することで問題を解決しようとします。

  • グリーン スレッドは、実際の OS レベルのスレッドではありません。これらは、並行性のためのユーザー空間の抽象化です。最も重要なことは、N 個のグリーン スレッドが 1 つの OS スレッドにマップされることです。これにより、GIL の問題が回避されます。

  • グリーン スレッドは、プリエンプティブにスケジュールされるのではなく、協調して互いに譲歩します。ネットワーク操作の場合、すべての呼び出しが非ブロックになるように、実行時にソケット ライブラリにパッチが適用されます (モンキー パッチ)。

  • そのため、イベントレット グリーン スレッドのプールを作成しても、実際には OS レベルのスレッドを 1 つしか作成していません。この単一の OS レベル スレッドがすべてのイベントレットを実行します。アイデアは、すべてのネットワーク呼び出しがブロックされていない場合、場合によっては Python スレッドよりも高速になるはずだということです。

概要

上記のプログラムの場合、「真の」同時実行性は、イベントレット モデル (1 つのプロセッサで実行される単一スレッド) よりも高速です (cpython バージョン、複数のプロセッサで実行される 5 つのスレッド)。

多くのスレッド/コアでパフォーマンスが低下する cpython ワークロードがいくつかあります (たとえば、サーバーに接続している 100 のクライアントがあり、クライアントごとに 1 つのスレッドがある場合)。Eventlet は、このようなワークロード向けの洗練されたプログラミング モデルであるため、いくつかの場所で使用されています。

于 2012-10-24T03:37:12.867 に答える
3

あなたの質問のタイトルは、「Python でのマルチスレッド プログラミングの利点は何ですか?」です。そのため、問題を解決しようとするのではなく、例を挙げています。2005 年に購入したペンティアム コア デュオで実行されている Python プログラムがあり、finance.yahoo.com から 500 個の csv ファイルをダウンロードする Windows XP を実行しています。 . スレッドを使用しない場合、標準の python スレッド (40 スレッド) を使用すると 2 分以上かかります。平均はそれぞれ約 1/4 秒で、3 ~ 4 秒です (これはウォール クロック時間であり、計算と I/O を含みます)。 )。各スレッド (ウォール クロック) の開始時間と終了時間を見ると、途方もない重なりがあります。Javaプログラムと同じものを実行していますが、パフォーマンスはpythonとjavaの間でほぼ同じです。また、curllib を使用する c++ と同じですが、curllib は Java や Python よりも少しだけ遅くなります。標準のpythonバージョン2.2.6を使用しています

于 2012-10-24T04:06:16.650 に答える
2

Python にはグローバル インタープリター ロックhttp://en.wikipedia.org/wiki/Global_Interpreter_Lockがあり、2 つのスレッドが同時に実行されるのを防ぎます。

cython などを使用している場合は、C 部分を同時に実行できるため、速度が向上します。

純粋な python プログラムでは、(実行できる計算量の点で) パフォーマンス上の利点はありませんが、多くの IO を実行するコードを記述する最も簡単な方法である場合があります (たとえば、ソケットの読み取りが完了するまでスレッドを待機させたままにしておくなど)。他のことをします)。

于 2012-10-24T03:08:10.007 に答える
1

プログラミング言語に関係なく、マルチスレッド プログラミングの主な利点は次のとおりです。

  1. 複数の CPU またはコアを備えたシステムの場合、すべての CPU で同時にアプリケーション コードを実行できます。したがって、たとえば、4 つの CPU を備えたシステムの場合、プロセスはマルチスレッド化によって最大 4 倍速く実行される可能性があります (ただし、通常のアプリケーションではスレッドがアクセスを同期する必要があるため、ほとんどの場合、それほど高速になる可能性はほとんどありません)。共有リソース、作成の競合)。

  2. 何らかの理由 (ディスク I/O、ユーザー入力、ネットワーク I/O) でプロセスをブロックする必要がある場合、1 つまたは複数のスレッドが I/O の完了を待ってブロックされている間に、他のスレッドが他の作業を行うことができます。このタイプの同時実行では、複数の CPU やコアは必要ないことに注意してください。単一の CPU で実行されているプロセスも、スレッド化によって大きなメリットを得ることができます。

これらの利点をプロセスに適用できるかどうかは、プロセスが何を行うかに大きく依存します。パフォーマンスが大幅に向上する場合もあれば、そうでない場合や、スレッド化されたバージョンの方が遅くなる場合もあります。優れた効率的なマルチスレッド アプリを作成するのは難しいことに注意してください。

さて、あなたは特に Python について尋ねているので、これらの利点が Python にどのように適用されるかについて説明しましょう。

  1. Python に存在するグローバル インタープリター ロックにより、複数の CPU でコードを並行して実行することはできません。GIL は、一度に 1 つのスレッドのみが Python コードを解釈することを保証するため、複数の CPU を最大限に活用する方法は実際にはありません。

  2. Python スレッドがブロック操作を実行すると、別のスレッドが CPU を取得して実行を継続しますが、最初のスレッドはブロックされて待機します。ブロッキング イベントが完了すると、ブロックされたスレッドが再開されます。したがって、これは Python スクリプトにマルチスレッドを実装する正当な理由です (このタイプの同時実行を実現する唯一の方法ではありませんが、ノンブロッキング I/O でも同様の結果が得られます)。

複数のスレッドを使用することでメリットが得られる例を次に示します。

  • 時間のかかる操作を実行している GUI プログラムには、アプリケーション ウィンドウの更新と応答性を維持し続けるスレッドがあり、長い操作の進行状況レポートとキャンセル ボタンを表示することさえあります。

  • ディスクからレコードを繰り返し読み取り、それらに対して何らかの処理を行い、最後にそれらをディスクに書き戻す必要があるプロセスは、スレッド化の恩恵を受けることができます。すでに読み取られていて、さらに別のスレッドが別のレコードをディスクに書き戻す可能性があります。プロセスがディスクの読み取りまたは書き込みを行っているときにスレッドがなければ、他に何も起こりません。GIL を持たない言語 (C++ など) の場合、それぞれが異なるコアで実行され、すべてが異なるレコードの処理を行う複数のスレッドを持つこともできるため、利点はさらに大きくなります。

これが役立つことを願っています!

于 2012-10-24T06:08:44.960 に答える
0

スレッドを追加しても、必ずしもプロセスが高速になるとは限りません。スレッドの管理に関連するオーバーヘッドがスレッドから得られるパフォーマンスの向上を上回る可能性があるためです。

CPU が多いマシンではなく、CPU が少ないマシンでこれを実行している場合、各スレッドの実行と実行をスワップするため、実行速度が遅くなることがあります。他の要因も作用している可能性があります。スレッドが、同時要求を処理できない他のサブシステムまたはハードウェア (シリアル ポートなど) にアクセスする必要がある場合、マルチスレッド化はパフォーマンスの向上には役立ちません。

于 2012-10-21T08:46:06.047 に答える