English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

pythonの並行プログラミングの多プロセス、多スレッド、非同期およびコルーチンの詳細

最近、Pythonの並行について学びましたので、マルチプロセス、マルチスレッド、アシンクリーノシとコルーチンのまとめをしました。
一、マルチスレッド

マルチスレッドは、プロセス内に複数のコントロールが存在することを許可し、複数の関数が同時にアクティブな状態にあることを可能にし、複数の関数の操作が同時に実行されるようにします。単一のCPUのコンピュータでも、異なるスレッドのインストラクション間で切り替え続けることで、マルチスレッドが同時に実行されている効果を生み出すことができます。

マルチスレッドは、並行(パラレル)システムに相当します。並行システムは通常、同時に複数のタスクを実行します。複数のタスクがリソースを共有し、特にある変数に同時に書き込む場合、シンクロナイズの問題を解決する必要があります。例えば、マルチスレッド鉄道チケット販売システム:一つのインストラクションはチケットが売り切れたかどうかをチェックし、もう一つのインストラクションは複数の窓口が同時にチケットを販売し、存在しないチケットを売る可能性があります。

並行処理の状況では、コマンドの実行順序はカーネルが決定します。同じスレッド内ではコマンドは順序に実行されますが、異なるスレッド間のコマンドの実行順序は明確には言えません。したがって、多スレッドの同期問題を考慮する必要があります。同期(同期)とは、ある期間内に特定のリソースにアクセスを許可するものであり、特定のスレッドのみがそのリソースにアクセスすることができます。

1、threadモジュール

2、threadingモジュール
threading.Threadを作成します。

余票があるかどうかとチケットを販売する際に、排他ロックを追加することで、一つのスレッドが余票がないと判断したばかりで、別のスレッドがチケット販売操作を実行するようなことがないようにします。

#! /usr/bin/python
#-* coding: utf-8 -*
# __author__ ="tyomcat"
import threading
import time
import os
def booth(tid):
  global i
  global lock
  while True:
    lock.acquire()
    if i != 0:
      i=i-1
      print "窓口:", tid, ", 剩余票数:", i
      time.sleep(1)
    else:
      print "Thread_id", tid, "No more tickets"
      os._exit(0)
    lock.release()
    time.sleep(1)
i = 100
lock = threading.Lock()
for k in range(10)
  new_thread = threading.Thread(target=booth, args=(k,))
  new_thread.start()

二、コルーチン(微スレッド、ファイナスと呼ばれることもあります)

コルーチンは、スレッドの強制的なスケジューリングとは異なり、協力型スケジューリングです。コルーチンもまた単一スレッドですが、従来の非同期を使用する必要がある場合に役立ちます。+コールバック方式で書かれた非人間的なコードは、見た目が同期のように書けることがあります。

1、コルーチンはPythonでは生成器(ジェネレータ)によって実現できます。

まず、生成器とyieldについてしっかりと理解する必要があります。

通常のPython関数を呼び出す場合、一般的には関数の最初の行のコードから実行し、return文、例外または関数の実行(Noneを暗黙的に返却することもできます)で終了します。

関数が制御権を呼び出し元に返却すると、すべてが終わります。しかし、時にはシーケンスを生成する関数を作成して「自分の作業を保存」することができます。これが生成器(yieldキーワードを使用する関数)です。

「シーケンスを生成する」ことができるのは、関数が通常の意味とは異なり返却していないからです。returnの意味は、関数が実行コードの制御権を呼び出し元に返却していることを示しています。一方、"yield"の意味は、制御権の移行が一時的で任意であることを示しており、関数が将来制御権を取り戻す予定です。

生産者を見てみましょう/消費者の例:

#! /usr/bin/python
#-* coding: utf-8 -*
# __author__ ="tyomcat"
import time
import sys
# 生産者
def produce(l):
  i=0
  while 1:
    if i < 10:
      l.append(i)
      yield i
      i=i+1
      time.sleep(1)
    else:
      return   
# 消費者
def consume(l):
  p = produce(l)
  while 1:
    try:
      p.next()
      while len(l) > 0:
        print l.pop()
    except StopIteration:
      sys.exit(0)
if __name__ == "__main__":
  l = []
  consume(l)

プログラムがproduceのyield iに到達したとき、ジェネレータを返し、実行を一時停止します。customの中でp.next()を呼び出すと、プログラムがproduceのyield iに戻り、実行を続けます。その結果、lに要素が追加され、print l.pop()を実行し、p.next()がStopIteration例外を発生させるまで繰り返します。

2Stackless Python

3greenletモジュール

greenletの実装は、Stackless Pythonに次ぐ性能で、Stackless Pythonよりも約半分遅く、他の方法に比べて約1桁速くです。実際、greenletは本物の並行メカニズムではなく、同一线程内で異なる関数の実行コードブロック間で切り替えを行い、「あなたが少し実行して、私が少し実行する」という実行を行います。そして、切り替えを行う際には、いつ切り替えるかおよびどこに切り替えるかを指定する必要があります。

4eventletモジュール

多プロセス
1子プロセス(subprocessパッケージ)

Pythonでは、subprocessパッケージを使用して、子プロセスをforkし、外部プログラムを実行します。

システムのコマンドを呼び出す際には、まずosモジュールを考慮します。os.system()とos.popen()を使用して操作を行いますが、これらの命令は非常に単純で、複雑な操作(例えば、実行中のコマンドに入力を提供したり、コマンドの输出を読取ったり、コマンドの実行状態を判断したり、複数のコマンドの並行管理など)を完了することができません。その場合、subprocessモジュールのPopenコマンドが必要な操作を効果的に完了できます。

>>>import subprocess
>>>command_line=raw_input()
ping -c 10 www.baidu.com
>>>args=shlex.split(command_line)
>>>p=subprocess.Popen(args)

subprocess.PIPEを使って複数のサブプロセスの入出力を接続してパイプライン(pipe)を構成します:

import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)

communicate() メソッドはstdoutとstderrからデータを読み出し、stdinにインプットします。

2、多プロセス(multiprocessingパッケージ)

(1)、multiprocessingパッケージはPythonの多プロセス管理パッケージです。threading.Threadと同様に、multiprocessing.Processオブジェクトを使ってプロセスを作成できます。

プロセスプール (Process Pool)は複数のプロセスを作成できます。

apply_async(func,args)  プロセスプールからプロセスを取得してfuncを実行します、argsはfuncの引数です。それはAsyncResultオブジェクトを返し、そのオブジェクトに対してget()メソッドを呼び出して結果を取得できます。

close()  プロセスプールは新しいプロセスを作成しません

join()  プロセスプール内のすべてのプロセスを待機します。joinメソッドを呼び出す前にPoolにclose()メソッドを呼び出す必要があります。

#! /usr/bin/env python
# -*- coding:utf-8  -*-
# __author__ == "tyomcat"
# "私のコンピュータには4個cpu"
from multiprocessing import Pool
import os, time
def 長時間タスク(name):
  print 'Run task %s (%s)...' % (name, os.getpid())
  start = time.time()
  time.sleep(3)
  end = time.time()
  print 'Task %s runs %0.2f seconds.' % (name, (end - start))
if __name__=='__main__':
  print 'Parent process %s.' % os.getpid()
  p = Pool()
  for i in range(4)
    p.apply_async(long_time_task, args=(i,))
  print 'Waiting for all subprocesses done...'
  p.close()
  p.join()
  print 'All subprocesses done.'

(2)、マルチプロセッシングで共有されるリソース

共有メモリとManagerオブジェクトを通じて:サーバーとしてのプロセスを1つ用意し、リソースを本当に保存するためにManagerを構築します。

他のプロセスはパラメータを渡すか、アドレスに基づいてManagerにアクセスし、接続が確立された後、サーバー上のリソースを操作することができます。

#! /usr/bin/env python
# -*- coding:utf-8  -*-
# __author__ == "tyomcat"
from multiprocessing import Queue,Pool
import multiprocessing,time,random
def write(q):
  for value in ['A','B','C','D']:
    print "Put %s to Queue!" % value
    q.put(value)
    time.sleep(random.random())
def read(q,lock):
  while True:
    lock.acquire()
    if not q.empty():
      value=q.get(True)
      print "Get %s from Queue" % value
      time.sleep(random.random())
    else:
      break
    lock.release()
if __name__ == "__main__":
  manager=multiprocessing.Manager()
  q=manager.Queue()
  p=Pool()
  lock=manager.Lock()
  pw=p.apply_async(write,args=(q,))
  pr=p.apply_async(read,args=(q,lock))
  p.close()
  p.join()
  印字します。
  すべてのデータが書き込まれ、読み込まれたことを印字します。

4. アシンクリーノス

スレッドでもプロセスでも、同期的な進行形を使用しており、ブロッキングが発生すると、性能が大幅に低下し、CPUの潜力が十分に活用されず、ハードウェア投資が無駄になります。さらに、ソフトウェアモジュールの硬直化、緊密な連携、分割が難しくなり、将来的な拡張や変更に不利益になります。

プロセスでもスレッドでも、ブロッキングや切り替えは常にシステムコールに陥ります。まずCPUはオペレーティングシステムのスケジューラを実行し、スケジューラがどのプロセス(スレッド)を実行するかを決定します。複数のスレッド間でアクセスを互換するコードを処理する場合、ロックを追加する必要があります。

現在人気のあるアシンクリーノスサーバーは、イベントドライブに基づいています(例:nginx)。

アシンクリーノスイベントドライブモデルでは、ブロッキング操作をアシンクリーノス操作に変換します。メインスレッドはこのアシンクリーノス操作を発起し、その結果を処理します。すべてのブロッキング操作がアシンクリーノス操作に変換されるため、理論的にはメインスレッドの大部分の時間が実際の計算タスクを処理するようになります。これにより、マルチスレッドのスケジューリング時間が減少し、このモデルの性能が通常良くなります。

これでこの記事はすべてです。皆様の学習に役立つことを願っています。また、呐喊ものしらせのサポートを多くお願いします。

声明:この記事の内容はインターネットから取得しており、著作権者に帰属します。インターネットユーザーによって自発的に貢献し、自己でアップロードされています。このサイトは所有権を持ちません。また、人工編集も行われていません。著作権侵害の疑いがある場合は、以下のメールアドレスにご連絡ください:notice#oldtoolbag.com(メールを送信する際には、#を@に置き換えてください。通報を行い、関連する証拠を提供してください。一旦確認がついたら、このサイトは侵害される内容をすぐに削除します。)

おすすめ