【初心者向け】PythonのConcurrent Executionでサブプロセスを同時実行!lpAttributeListでさらに高度な制御


subprocess.STARTUPINFO.lpAttributeList は、Python の concurrent.futures モジュールでサブプロセスを同時に実行する場合に、Windows システムで高度な制御を提供するために使用される属性です。これは、サブプロセスの起動方法を詳細に設定するためのオプションのリストであり、セキュリティ、リソース管理、および互換性の向上に役立ちます。

機能

lpAttributeList は、以下の機能を提供します。

  • 互換性: 以前のバージョンの Windows との互換性を維持するために、特定の動作を制御できます。
  • セキュリティ: セキュリティコンテキストをサブプロセスに伝達し、特権レベルを制御したり、アクセス許可を制限したりできます。
  • ハンドル: 標準入力、標準出力、標準エラー以外のファイルハンドルをサブプロセスに渡すことができます。これにより、サブプロセス間でデータを共有したり、外部リソースへのアクセスを制御したりできます。

使用方法

lpAttributeList を使用するには、以下の手順に従います。

  1. subprocess.STARTUPINFO オブジェクトを作成します。
  2. lpAttributeList 属性に辞書を割り当てます。
  3. 辞書内に、必要なオプションと対応する値を設定します。
  4. subprocess.Popen() 関数を使用して、サブプロセスを開始し、startupinfo パラメーターに STARTUPINFO オブジェクトを渡します。

以下の例は、lpAttributeList を使用して、標準入力ハンドルを別のサブプロセスに渡す方法を示しています。

import subprocess

def child1():
    # 標準入力からデータを読み取る
    data = input()
    print(f"Child 1 received: {data}")

def child2():
    # 標準出力にデータを出力する
    print("Data from child 2")

startupinfo = subprocess.STARTUPINFO()
startupinfo.lpAttributeList["handle_list"] = [subprocess.STD_INPUT, child2_proc.stdout.fileno()]

child1_proc = subprocess.Popen(child1, startupinfo=startupinfo)
child2_proc = subprocess.Popen(child2)

child1_proc.wait()
child2_proc.wait()

この例では、child1 プロセスは child2 プロセスの標準出力からデータを読み取ることができます。これは、パイプを使用して 2 つのサブプロセス間でデータを共有する一般的な方法です。

  • lpAttributeList を使用すると、複雑なコードになる可能性があります。詳細については、subprocess モジュールのドキュメントを参照してください。
  • lpAttributeList を使用するには、subprocess モジュールの最新バージョンが必要です。
  • lpAttributeList は Windows システムでのみ使用できます。


import subprocess

def child1():
    # 標準入力からデータを読み取る
    data = input()
    print(f"Child 1 received: {data}")

def child2():
    # 標準出力にデータを出力する
    print("Data from child 2")

startupinfo = subprocess.STARTUPINFO()
startupinfo.lpAttributeList["handle_list"] = [subprocess.STD_INPUT, child2_proc.stdout.fileno()]

child1_proc = subprocess.Popen(child1, startupinfo=startupinfo)
child2_proc = subprocess.Popen(child2)

child1_proc.wait()
child2_proc.wait()

説明

  1. child1child2 という 2 つの関数を作成します。
  2. child1 関数は、標準入力からデータを読み取り、コンソールに出力します。
  3. child2 関数は、"Data from child 2" という文字列を標準出力に出力します。
  4. startupinfo オブジェクトを作成します。
  5. lpAttributeList 属性に辞書を割り当てます。
  6. 辞書内に、handle_list キーと対応するリストを設定します。このリストは、2 つのハンドルを含みます。
    • 最初のハンドルは subprocess.STD_INPUT であり、標準入力ハンドルを表します。
    • 2 番目のハンドルは child2_proc.stdout.fileno() であり、child2 プロセスの標準出力ハンドルを表します。
  7. subprocess.Popen() 関数を使用して、child1 プロセスと child2 プロセスを起動します。
  8. startupinfo パラメーターに startupinfo オブジェクトを渡します。
  9. 両方のプロセスの終了を待ちます。

実行方法

このコードを実行するには、以下の手順に従います。

  1. コードを保存します。
  2. ターミナルを開き、保存したファイルに移動します。
  3. 以下のコマンドを実行します。
python your_script.py

出力

以下の出力がコンソールに表示されます。

Data from child 2
Child 1 received: Data from child 2

解説

この例では、lpAttributeList を使用して、child1 プロセスが child2 プロセスの標準出力からデータを読み取れるようにします。これは、パイプを使用して 2 つのサブプロセス間でデータを共有する一般的な方法です。

  • この例は、複雑なコードになる可能性があります。詳細については、subprocess モジュールのドキュメントを参照してください。
  • この例は、subprocess モジュールの最新バージョンが必要です。
  • この例は、Windows システムでのみ動作します。
  • 以前のバージョンの Windows との互換性を維持するために、特定の動作を制御する
  • セキュリティコンテキストをサブプロセスに伝達する


サブプロセス間通信用のライブラリを使用する

pipe モジュールや multiprocessing モジュールなどのライブラリを使用して、サブプロセス間でデータを共有したり、同期したりすることができます。これらのライブラリは、subprocess.STARTUPINFO.lpAttributeList よりも使いやすく、移植性も高いです。

例:pipe モジュールを使用して、標準入力を別のサブプロセスに渡す

import subprocess
import os

def child1():
    # 標準入力からデータを読み取る
    data = os.read(child1_pipe[0], 1024).decode()
    print(f"Child 1 received: {data}")

def child2():
    # 標準出力にデータを出力する
    print("Data from child 2")
    os.write(child2_pipe[1], b"Data from child 2\n")

child1_pipe, child2_pipe = os.pipe()

child1_proc = subprocess.Popen(child1, stdin=child1_pipe)
child2_proc = subprocess.Popen(child2, stdout=child2_pipe)

child1_proc.wait()
child2_proc.wait()

os.close(child1_pipe[0])
os.close(child2_pipe[1])

シェルスクリプトを使用する

複雑なタスクを実行する場合は、シェルスクリプトを使用してサブプロセスを起動することができます。シェルスクリプトは、subprocess.STARTUPINFO.lpAttributeList よりもシンプルで読みやすい場合があります。

例:シェルスクリプトを使用して、標準入力を別のサブプロセスに渡す

#!/bin/bash

child1() {
    # 標準入力からデータを読み取る
    read data
    echo "Child 1 received: $data"
}

child2() {
    # 標準出力にデータを出力する
    echo "Data from child 2"
}

pipe=$(mkfifo)

child1 < $pipe & child2 > $pipe

wait $child1
wait $child2

rm $pipe

ctypes モジュールを使用する

ctypes モジュールを使用して、Windows API 関数を直接呼び出すことができます。これは、高度な制御が必要な場合に役立ちます。

例:ctypes モジュールを使用して、標準入力を別のサブプロセスに渡す

import subprocess
import ctypes

def child1():
    # 標準入力からデータを読み取る
    data = ctypes.windll.kernel32.ReadConsoleInput(ctypes.byref(hStdIn), lpBuffer=ctypes.create_string_buffer(1024), nNumberOfBytesToRead=ctypes.c_int(1024), lpNumberOfBytesRead=ctypes.pointer(ctypes.c_int()))
    if data.lpNumberOfBytesRead[0] > 0:
        print(f"Child 1 received: {data.lpBuffer.raw[:data.lpNumberOfBytesRead[0]]}")

def child2():
    # 標準出力にデータを出力する
    print("Data from child 2")

hStdIn = ctypes.windll.kernel32.GetStdHandle(ctypes.c_int(subprocess.STD_INPUT))

child1_proc = subprocess.Popen(child1)
child2_proc = subprocess.Popen(child2)

child1_proc.wait()
child2_proc.wait()

注意事項

  • これらの代替方法は、より複雑なコードになる可能性があります。詳細については、各ライブラリまたはモジュールのドキュメントを参照してください。
  • これらの代替方法は、subprocess.STARTUPINFO.lpAttributeList よりも機能が制限される場合があります。
  • 上記の代替方法は、すべて Windows システムでのみ動作します。

subprocess.STARTUPINFO.lpAttributeList は強力なツールですが、複雑で使いにくい場合があります。上記の代替方法を検討することで、コードをよりシンプルで理解しやすくすることができます。

  • [pipe — Two-way pipes](