17. 初心者から上級者までの Python - プロセスとスレッドの使用

目次

プロセスを作成する 4 つの方法

multiprocessing モジュールの Process クラスはプロセスを作成します

Process クラスのサブクラスはプロセスを作成します

マルチプロセッシングモジュールのプロセスプールPoolクラスを使用してプロセスを作成する

プロセス間通信

マルチプロセッシング モジュールの Queue クラスは、キュー関連の操作を実装するために使用されます。

共通操作

プロセス間通信にキューを使用する

スレッドを作成する

スレッドモジュール Thread クラスがスレッドを作成する        

Thread サブクラスを使用してスレッドを作成する

スレッド間の通信

ミューテックスロックの使用       

キューを使用したスレッド間の通信


Python でプロセスを使用する: Python で対応するモジュールを呼び出し、このモジュールを使用してオペレーティング システムでプロセスを操作します。

プロセスを作成する 4 つの方法

  1. os.fork() 関数: Windows をサポートしていません
  2. multiprocessing マルチプロセス モジュール Process クラス
  3. Process クラスのサブクラス
  4. プールプロセスプール

multiprocessing モジュールの Process クラスはプロセスを作成します

使用法:

Process([group [, target [, name [, args [, kwargs]]]]])
    #group: パラメーターは使用されず、値は常に None です。
    #target: 現在のプロセスの開始時に実行される呼び出し可能オブジェクトを表します。
    #name: は、現在のプロセス インスタンスのエイリアスです。
    #args: ターゲット関数に渡されるパラメータタプルを表します。
    #kwargs: ターゲット関数に渡されるパラメーター ディクショナリを表します。

Process クラスで一般的に使用されるメソッドとプロパティ:

    方法:

          is_alive(): プロセス インスタンスがまだ実行中かどうかを判断します;
          join([timeout]): プロセス インスタンスが実行を終了するまで待つか、または何秒待つか;
          start(): プロセス インスタンスを開始します (子を作成します) process);
          run(): 指定されていない場合、target パラメータが指定されている場合、このオブジェクトで start() メソッドが呼び出されると、オブジェクト内の run() メソッドが実行されます; terminate(): タスクが実行されているかどうかに関係なく、ただちに終了します
          。完了しました。

    属性:

          name: 現在のプロセス インスタンスのエイリアス、デフォルトは Process-N (N は 1 から増加する整数)、
          pid: 現在のプロセス インスタンスの PID 値。

例1

注: 以下のプログラムを実行した結果は、メインプロセスが先に実行され、サブプロセスは1秒スリープするため最後に実行され、メインプロセスとサブプロセスが同時に実行されます。

from multiprocessing import Process
import time


def test(intervel):
    time.sleep(intervel)  # 睡眠
    print("我是子进程")


def main():
    print('主程序开始')
    p = Process(target=test, args=(1,))  # 设置要调用的子进程和传递参数1让子进程睡眠1秒
    p.start()
    print('主进程结束')


if __name__ == '__main__':
    main()


#主程序开始
#主进程结束
#我是子进程

例 2:

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os

#两个子进程将会调用的两个方法
def  child_1(interval):
    print("子进程(%s)开始执行,父进程为(%s)" % (os.getpid(), os.getppid()))
    t_start = time.time()   # 计时开始
    time.sleep(interval)    # 程序将会被挂起interval秒
    t_end = time.time()     # 计时结束
    print("子进程(%s)执行时间为'%0.2f'秒"%(os.getpid(),t_end - t_start))

def  child_2(interval):
    print("子进程(%s)开始执行,父进程为(%s)" % (os.getpid(), os.getppid()))
    t_start = time.time()   # 计时开始
    time.sleep(interval)    # 程序将会被挂起interval秒
    t_end = time.time()     # 计时结束
    print("子进程(%s)执行时间为'%0.2f'秒"%(os.getpid(),t_end - t_start))

if __name__ == '__main__':
    print("------父进程开始执行-------")
    print("父进程PID:%s" % os.getpid())                  # 输出当前程序的ID
    p1=Process(target=child_1,args=(1,))                   # 实例化进程p1
    p2=Process(target=child_2,name="mrsoft",args=(2,))     # 实例化进程p2
    p1.start()  # 启动进程p1
    p2.start()  # 启动进程p2
    #同时父进程仍然往下执行,如果p2进程还在执行,将会返回True
    print("p1.is_alive=%s"%p1.is_alive())
    print("p2.is_alive=%s"%p2.is_alive())
    #输出p1和p2进程的别名和pid
    print("p1.name=%s"%p1.name)
    print("p1.pid=%s"%p1.pid)
    print("p2.name=%s"%p2.name)
    print("p2.pid=%s"%p2.pid)
    print("------等待子进程-------")
    p1.join() # 等待p1进程结束
    p2.join() # 等待p2进程结束
    print("------父进程执行结束-------")
###########################################
##------父进程开始执行-------
##父进程PID:13668
##p1.is_alive=True
##p2.is_alive=True
##p1.name=Process-1
##p1.pid=16536
##p2.name=mrsoft
##p2.pid=11176
##------等待子进程-------
##子进程(16536)开始执行,父进程为(13668)
##子进程(11176)开始执行,父进程为(13668)
##子进程(16536)执行时间为'1.01'秒
##子进程(11176)执行时间为'2.00'秒
##------父进程执行结束-------

Process クラスのサブクラスはプロセスを作成します

いくつかの単純な小さなタスクの場合、マルチプロセッシング モジュールの Precess クラスを使用して複数のサブプロセスを作成できますが、複雑なタスクを処理する場合は、通常、Process クラスを定義し、他の n 個のサブクラスにこの Process クラスを継承させます。 n個のプロセスを作成しました

例: Process サブクラスを使用して 2 つのサブプロセスを作成し、サブプロセスの実行時間を記録します。

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os

#继承Process类
class SubProcess(Process):
    # 由于Process类本身也有__init__初始化方法,这个子类相当于重写了父类的这个方法
    def __init__(self,interval,name=''):
        Process.__init__(self)      # 调用Process父类的初始化方法
        self.interval = interval    # 接收参数interval
        if name:                    # 判断传递的参数name是否存在
            self.name = name        # 如果传递参数name,则为子进程创建name属性,否则使用默认属性
    #重写了Process类的run()方法
    def run(self):
        print("子进程(%s) 开始执行,父进程为(%s)"%(os.getpid(),os.getppid()))
        t_start = time.time()
        time.sleep(self.interval)
        t_stop = time.time()
        print("子进程(%s)执行结束,耗时%0.2f秒"%(os.getpid(),t_stop-t_start))

if __name__=="__main__":
    print("------父进程开始执行-------")
    print("父进程PID:%s" % os.getpid())                  # 输出当前程序的ID
    p1 = SubProcess(interval=1,name='mrsoft')
    p2 = SubProcess(interval=2)
    #对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法,所以这里会执行p1.run()
    p1.start()  # 启动进程p1
    p2.start()  # 启动进程p2
    # 输出p1和p2进程的执行状态,如果真正进行,返回True,否则返回False
    print("p1.is_alive=%s"%p1.is_alive())
    print("p2.is_alive=%s"%p2.is_alive())
    #输出p1和p2进程的别名和pid
    print("p1.name=%s"%p1.name)
    print("p1.pid=%s"%p1.pid)
    print("p2.name=%s"%p2.name)
    print("p2.pid=%s"%p2.pid)
    print("------等待子进程-------")
    p1.join() # 等待p1进程结束
    p2.join() # 等待p2进程结束
    print("------父进程执行结束-------")
##############################
##------父进程开始执行-------
##父进程PID:17252
##p1.is_alive=True
##p2.is_alive=True
##p1.name=mrsoft
##p1.pid=19728
##p2.name=SubProcess-2
##p2.pid=18436
##------等待子进程-------
##子进程(19728) 开始执行,父进程为(17252)
##子进程(18436) 开始执行,父进程为(17252)
##子进程(19728)执行结束,耗时1.00秒
##子进程(18436)执行结束,耗时2.01秒
##------父进程执行结束-------

マルチプロセッシングモジュールのプロセスプールPoolクラスを使用してプロセスを作成する

利点: Process クラスを使用してプロセスを作成するよりもリソースを節約できます。

Pool クラスの一般的なメソッド:

        apply_async(func[, args[, kwds]]): ノンブロッキング モードを使用して func 関数を呼び出します (並列実行、ブロッキング モードでは、次のプロセスを実行する前に前のプロセスが終了するまで待機する必要があります)。 func は関数名です。 args は func に渡される値です。パラメータはタプルであり、kwds は func に渡される辞書です。
        apply(func[, args[, kwds]]): ブロッキング モードを使用して func 関数を呼び出します。
        close(): プールを閉じて、新しいタスクを受け入れないようにする。
        terminate(): タスクが完了したかどうかに関係なく、ただちに終了します。
        join(): メイン プロセスはブロックされ、子プロセスの終了を待ちます。閉じるか終了した後に使用する必要があります。

例: プロセス プールを使用して子プロセスを作成する

# -*- coding=utf-8 -*-
from multiprocessing import Pool
import os, time

def task(name):
    print('子进程(%s)执行task %s ...' % ( os.getpid() ,name))
    time.sleep(1)       # 休眠1秒

if __name__=='__main__':
    print('父进程(%s).' % os.getpid())
    p = Pool(3) #进程池设置最多有三个进程   
    for i in range(10):                 # 从0开始循环10次    
        p.apply_async(task, args=(i,))  # 使用非阻塞方式调用task()函数   
    print('等待所有子进程结束...')
    p.close()   # 关闭进程池,关闭后p不再接收新的请求
    p.join()    # 等待子进程结束
    print('所有子进程结束.')
########################################
##父进程(16252).
##等待所有子进程结束...
##子进程(11076)执行task 0 ...
##子进程(17112)执行task 1 ...
##子进程(17636)执行task 2 ...
##子进程(11076)执行task 3 ...
##子进程(17112)执行task 4 ...
##子进程(17636)执行task 5 ...
##子进程(11076)执行task 6 ...
##子进程(17112)执行task 7 ...
##子进程(17636)执行task 8 ...
##子进程(11076)执行task 9 ...
##所有子进程结束.

プロセス間通信

例: 変数情報が共有されているかどうか、およびサブプロセス間の直接通信を検証する

from multiprocessing import Process

def plus():
    print("--子进程1开始--")
    global g_num
    g_num += 50
    print('g_num is %d' % g_num)
    print("--子进程结束--")

def minus():
    print("--子进程2开始--")
    global g_num
    g_num -= 50
    print('g_num is %d' % g_num)
    print("--子进程结束--")

g_num = 100
if __name__ == '__main__':
    print("--主进程开始--")
    p1 = Process(target=plus)
    p2 = Process(target=minus)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("--主进程结束--")

######################################################
##--主进程开始--
##--子进程1开始--
##g_num is 150  
##--子进程结束--
##--子进程2开始--
##g_num is 50   
##--子进程结束--
##--主进程结束--

        結果: 子プロセスは変数情報を共有できず、直接通信できません。

        結論: メイン プロセスと子プロセスの間、または子プロセスと子プロセスの間には直接通信はありません。これは、各プロセスが独自のアドレス空間、メモリ、データ スタック、およびステータスを記録するその他の補助データを持っているためです。

プロセス間通信方式:キューとパイプ

        キュー: キューでは先入れ先出しの原則に従います。

マルチプロセッシング モジュールの Queue クラスは、キュー関連の操作を実装するために使用されます。

共通操作

Queue.qsize(): 現在のキューに含まれるメッセージの数を返します。
Queue.empty(): キューが空の場合は True を返し、それ以外の場合は False を返します。
Queue.full(): キューがいっぱいの場合は True を返し、それ以外の場合は False を返します。
Queue.get([block[, timeout]]): キュー内のメッセージを取得し、キューから削除します。block のデフォルト値は True です。timeout はタイムアウトです。キューが空で待機時間がタイムアウトを超えると、例外がスローされます。Queue.get_nowait
(): Queue.get(False) と同等です。キューが空の場合は、すぐに例外がスローされます。 .
Queue.put(item,[block [, timeout]]): 項目メッセージをキューに書き込みます、block のデフォルト値は True です。
Queue.put_nowait(item): Queue.put(item, False) と同等。

例:

from multiprocessing import Queue

if __name__ == '__main__':
    q = Queue(3) #列队中最多有3条信息,默认为无限长
    q.put("消息1")
    q.put("消息2")
    print('列队是否已满:%s' % q.full())
    q.put("消息3")
    print('列队是否已满:%s' % q.full())
    try:
        q.put("消息4",True,2) #测试写入第4条消息等待2秒后异常
    except:
        print("消息列队已满,现有消息数量%s" % q.qsize())

    if not q.empty():
        print("--从列队中取消息--")
        for i in  range(q.qsize()):
            print(q.get_nowait())
    if not q.full():
        q.put("消息2")
        print(q.qsize())

#######################################
##列队是否已满:False
##列队是否已满:True
##消息列队已满,现有消息数量3
##--从列队中取消息--
##消息1
##消息2
##消息3
##1

プロセス間通信にキューを使用する

例: キューへのデータの書き込みと読み取りを別々に行う

        # 2 つのプロセスを作成します。1 つはキューにデータを書き込むため、もう 1 つはキューにデータを読み取るためです。

# -*- coding: utf-8 -*-
from multiprocessing import Process, Queue

# 向队列中写入数据
def write_task(q):
    if not q.full():
        for i in range(5):
            message = "消息" + str(i)
            q.put(message)
            print("写入:%s"%message)
# 从队列读取数据
def read_task(q):
    while not q.empty():
        print("读取:%s" % q.get(True,2)) # 等待2秒,如果还没读取到任何消息,则抛出"Queue.Empty"异常

if __name__ == "__main__":
    print("-----父进程开始-----")
    q = Queue()  # 父进程创建Queue,并传给各个子进程
    pw = Process(target=write_task, args=(q,))      # 实例化写入队列的子进程,并且传递队列
    pr = Process(target=read_task, args=(q,))       # 实例化读取队列的子进程,并且传递队列
    pw.start()   # 启动子进程 pw,写入
    pr.start()   # 启动子进程 pr,读取
    pw.join()    # 等待 pw 结束
    pr.join()    # 等待 pr 结束
    print("-----父进程结束-----")
####################################################
##-----父进程开始-----
##写入:消息0
##写入:消息1
##写入:消息2
##写入:消息3
##写入:消息4
##读取:消息0
##读取:消息1
##读取:消息2
##读取:消息3
##读取:消息4
##-----父进程结束-----

スレッド: オペレーティング システムが計算スケジュールを実行できる最小単位。これはプロセスに含まれ、プロセス内の実際の操作単位です。プロセス内で複数のスレッドを同時に実行でき、各スレッドは異なるタスクを実行できます。

プロセスとスレッドの基本的な違いと関係:

        プロセスはオペレーティング システムのリソース割り当ての最小単位であり、スレッドはタスクのスケジュールと実行の最小単位です。

        プロセスには複数のスレッドを含めることができます

        スレッドはプロセスリソースを共有します

スレッドを作成する

次の 2 つの方法があります。

        スレッドモジュール Thread クラスがスレッドを作成する

        Thread サブクラスを使用してスレッドを作成する

文法:

        Thread([グループ [, ターゲット [, 名前 [, 引数 [, kwargs]]]]])

            #group: パラメーターは使用されず、値は常に None です。
            #target: 現在のスレッドの開始時に実行される呼び出し可能オブジェクトを示します。
            #name: は、現在のスレッド インスタンスのエイリアスです。
            #args: ターゲット関数に渡されるパラメータタプルを表します。
            #kwargs: ターゲット関数に渡されるパラメータ辞書を示します。

スレッドモジュール Thread クラスがスレッドを作成する        

例:

import threading
import time
def thread():
    for i in range(3):
        time.sleep(1)
        print("thread name is %s" % threading.current_thread().name)

if __name__ == '__main__':
    print("--主线程开始--")
    threads = [threading.Thread(target = thread) for i in range(4)]
    for t in  threads:
        t.start()
    for t in threads:
        t.join()
    print("--主进程结束--")

#############################################
##--主线程开始--
##thread name is Thread-2 (thread)
##thread name is Thread-3 (thread)
##thread name is Thread-1 (thread)
##thread name is Thread-4 (thread)
##thread name is Thread-4 (thread)
##thread name is Thread-1 (thread)
##thread name is Thread-2 (thread)
##thread name is Thread-3 (thread)
##thread name is Thread-3 (thread)
##thread name is Thread-2 (thread)
##thread name is Thread-4 (thread)
##thread name is Thread-1 (thread)
##--主进程结束--  

Thread サブクラスを使用してスレッドを作成する

例:

# -*- coding: utf-8 -*-
import threading
import time
class SubThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "子线程"+self.name+'执行,i='+str(i) #name属性中保存的是当前线程的名字
            print(msg)
if __name__ == '__main__':
    print('-----主线程开始-----')
    t1 = SubThread() # 创建子线程t1
    t2 = SubThread() # 创建子线程t2
    t1.start()      # 启动子线程t1
    t2.start()      # 启动子线程t2
    t1.join()       # 等待子线程t1
    t2.join()       # 等待子线程t2
    print('-----主线程结束-----')
#########################################
##-----主线程开始-----
##子线程Thread-2执行,i=0
##子线程Thread-1执行,i=0
##子线程Thread-2执行,i=1
##子线程Thread-1执行,i=1
##子线程Thread-1执行,i=2
##子线程Thread-2执行,i=2
##-----主线程结束-----  

スレッド間の通信

例: スレッド間通信のテスト

from threading import Thread

def plus():
    print("--子线程1开始--")
    global g_num
    g_num += 50
    print('g_num is %d' % g_num)
    print("--子线程结束--")

def minus():
    print("--子线程2开始--")
    global g_num
    g_num -= 50
    print('g_num is %d' % g_num)
    print("--子线程结束--")

g_num = 100
if __name__ == '__main__':
    print("--主进程开始--")
    t1 = Thread(target=plus)
    t2 = Thread(target=minus)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("--主进程结束--")
######################################
##--主进程开始--
##--子线程1开始--
##g_num is 150   
##--子线程2开始--
##g_num is 100   
##--子线程结束-- 
##--子线程结束-- 
##--主进程结束-- 

上記のプログラムは、同じプロセス内の複数のスレッドがデータを共有できることを示していますが、複数のスレッドが変数を一緒に使用すると混乱が生じるため、安全ではないスレッドです。

ロック: Python で安全なスレッドを実現するために、スレッド ロックが使用されます。スレッドのセキュリティは、ミューテックス ロックとも呼ばれるロックとロック解除によって確保されます。

ミューテックス ロック: ミューテックス ロックを使用すると、複数のスレッドが特定のメモリ領域を同時に読み書きすることを防ぐことができます。ミューテックス ロックにより、リソースにロックまたは非ロックの状態が導入されます。スレッドが共有データを変更する場合は、最初にそのデータをロックする必要があります。このとき、リソースのステータスはロックされます。スレッドがリソースを解放するまで、つまりロックを解除するまで、他のスレッドは共有データを変更できません。リソースのロックが解除されます。この時点で、他のスレッドがリソースを再度ロックする可能性があります。ミューテックス ロックは、一度に 1 つのスレッドだけが読み取りおよび書き込み操作を実行することを保証するため、マルチスレッド状況でのデータの正確性が保証されます。

ミューテックスロックの使用       

ロックを作成します: mutex = threading.Lock()

ロック: metex.acquire([ブロッキング])

                #blocking: はブール値で、False に設定すると、ロックが取得できない場合はすぐに False を返し、ロックの取得に成功した場合は True を返します。  

ロックを解放します: metex.release() #ロックがないときにロックの解放を呼び出すと、例外がスローされます。

例:マルチスレッドとミューテックスロックによる複数人分の映画チケット注文機能の実装

from threading import Thread,Lock
import time
n=100 # 共100张票                      

def task():
    global n
    mutex.acquire()             # 上锁
    temp=n                      # 赋值给临时变量
    time.sleep(0.1)             # 休眠0.1秒
    n=temp-1                    # 数量减1 
    print('购买成功,剩余%d张电影票'%n)
    mutex.release()             # 释放锁

if __name__ == '__main__':
    mutex=Lock()                # 实例化Lock类
    t_l=[]                      # 初始化一个列表
    for i in range(10):         
        t=Thread(target=task)   # 实例化线程类    
        t_l.append(t)           # 将线程实例存入列表中
        t.start()               # 创建线程
    for t in t_l:               
        t.join()                # 等待子线程结束    
#######################################
##购买成功,剩余99张电影票
##购买成功,剩余98张电影票
##购买成功,剩余97张电影票
##购买成功,剩余96张电影票
##购买成功,剩余95张电影票
##购买成功,剩余94张电影票
##购买成功,剩余93张电影票
##购买成功,剩余92张电影票
##购买成功,剩余91张电影票
##购买成功,剩余90张电影票

デッドロックの問題に注意してください。2 つのスレッドが互いに対応するリソースを同時に解放するのを待っているため、デッドロック現象が発生します。

キューを使用したスレッド間の通信

キューを使用してプロデューサーとコンシューマーのパターンをシミュレートする

        注: プロセスのキューイングには、キュー モジュールから Queue クラスをインポートする必要があります。

# -*- coding: utf-8 -*-
from queue import Queue
import random,threading,time

#生产者类
class Producer(threading.Thread):
    def __init__(self, name,queue):
        threading.Thread.__init__(self, name=name)
        self.data=queue
    def run(self):
        for i in range(5):
            print("生产者%s将产品%d加入队列!" % (self.getName(), i))
            self.data.put(i)
            time.sleep(random.random())
        print("生产者%s完成!" % self.getName())

#消费者类
class Consumer(threading.Thread):
    def __init__(self,name,queue):
        threading.Thread.__init__(self,name=name)
        self.data=queue
    def run(self):
        for i in range(5):
            val = self.data.get()
            print("消费者%s将产品%d从队列中取出!" % (self.getName(),val))
            time.sleep(random.random())
        print("消费者%s完成!" % self.getName())

if __name__ == '__main__':
    print('-----主线程开始-----')
    queue = Queue()        # 实例化队列
    producer = Producer('Producer',queue)   # 实例化线程Producer,并传入队列作为参数
    consumer = Consumer('Consumer',queue)   # 实例化线程Consumer,并传入队列作为参数
    producer.start()    # 启动线程Producer
    consumer.start()    # 启动线程Consumer
    producer.join()     # 等待线程Producer结束
    consumer.join()     # 等待线程Consumer结束
    print('-----主线程结束-----')
#################################################################
##-----主线程开始-----
##生产者Producer将产品0加入队列!
##消费者Consumer将产品0从队列中取出!
##生产者Producer将产品1加入队列!
##生产者Producer将产品2加入队列!
##消费者Consumer将产品1从队列中取出!
##生产者Producer将产品3加入队列!
##消费者Consumer将产品2从队列中取出!
##消费者Consumer将产品3从队列中取出!
##生产者Producer将产品4加入队列!
##生产者Producer完成!
##消费者Consumer将产品4从队列中取出!
##消费者Consumer完成!
##-----主线程结束-----
            

​​​​​​​

おすすめ

転載: blog.csdn.net/weixin_43812198/article/details/131853141