Prozessbetrieb der nebenläufigen Programmierung in Python

Vorwort

Dieser Artikel beschreibt, wie Sie einen Prozess in Python starten.

Fortschritt ansehen

Auf einem Computer laufen viele Prozesse, wie unterscheidet und verwaltet die Computerverarbeitung diese Prozessserver: Der Computer weist jedem laufenden Programm, dh einem Prozess, eine PID-Nummer zu.

Der Windows-Systemcomputer gibt cmd ein und tasklistzeigt die Prozessnummer aller Prozesse an.

Der Mac- oder Linux-Systemcomputer gibt die Terminaleingabe ein, ps auxum den Prozess anzuzeigen.

Wie kann die PID des aktuellen Prozesses im Code angezeigt werden? Und wie starte ich den Prozess im Python-Code? multiprocessingModule sind verfügbar .

Multiprocessing-Modul

Dieses von python bereitgestellte multiprocessing模块Modul kann zum Öffnen von Unterprozessen, zum Anzeigen der aktuellen Prozessnummer usw. verwendet werden und stellt Komponenten wie Process, Queue, Pipe und Lock bereit.

Untergeordneten Prozess öffnen – Verwendung der Process-Klasse

Zu beachten ist, dass unter dem Windows-Betriebssystem der Startvorgang if__name__ == '__main__' unter dem Zweig stehen muss, der Originaltext lautet wie folgt:

Da Windows keinen Fork hat, startet das Multiprocessing-Modul einen neuen Python-Prozess und importiert das aufrufende Modul. Wenn Process() beim Import aufgerufen wird, löst dies eine unendliche Folge neuer Prozesse aus (oder bis Ihre Maschine keine Ressourcen mehr hat). Dies ist der Grund dafür, Aufrufe von Process() darin zu verstecken.

Es gibt zwei Möglichkeiten, einen Prozess in Python mit der Process-Klasse zu erstellen und zu öffnen:

Methode 1: Häufig verwendete Methode

import time
import os
from multiprocessing import Process, current_process
'''
current_process().pid可以获取当前进程的进程号PID
os.getpid()也可以获取当前进程的进程号PID
'''

# 子进程的任务
def func(name):
    print(f'{name} start')
    time.sleep(2)
    print(f'func {current_process().pid},current——process获取进程号')
    print(f'func {os.getpid()},OS获取进程号')
    print(f'{name} end')

if __name__ == '__main__':
    # 创建子进程,target是子进程需要执行的任务,args是任务的参数,为元组形式
    p = Process(target=func, args=('zhuang', ))
    # 开启子进程
    p.start()
    # 执行当前文件就是主进程
    print(f'current_process获取进程号{current_process().pid},主进程结束')
    print(f'os获取进程号{os.getpid()},主进程结束')
    
# 代码运行结果
current_process获取进程号2868,主进程结束
os获取进程号2868,主进程结束
zhuang start
func 26520,current——process获取进程号
func 26520,OS获取进程号
zhuang end

Methode 2: Erben Sie die Process-Klasse und schreiben Sie die run-Methode der übergeordneten Klasse neu

import time
import os
from multiprocessing import Process, current_process

# 子进程的任务
def func(name):
    print(f'{name} start')
    time.sleep(2)
    print(f'func {current_process().pid},current——process获取进程号')
    print(f'func {os.getpid()},OS获取进程号')
    print(f'{name} end')
    
    
class MyProcess(Process):

    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):			# start会自动调用run
        func(self.name)
        
if __name__ == '__main__':
    p = MyProcess('zhuang')
    p.start()   # p.run()只有通过start开启的才是子进程,通过run开启的不是子进程
    print('主进程结束')

Durch die Ausführungsergebnisse des obigen Codes können wir feststellen, dass der Hauptprozess nicht darauf wartet, dass der untergeordnete Prozess die Ausführung beendet, was eine asynchrone Operation ist. Wir können mehrere untergeordnete Prozesse gleichzeitig öffnen:

from multiprocessing import Process

def func(name):
    print(f'{name} is start')
    time.sleep(2)
    print(f'{name} is done')


if __name__ == '__main__':
    for i in range(1, 4):
        p = Process(target=func, args=(f'p{i}', i))
        p.start()
    print('主进程结束')
    
# 输出:
主进程结束
p3 is start
p1 is start
p2 is start
p1 is done
p2 is done
p3 is done

Starten von drei Teilprozessen gleichzeitig, die Reihenfolge des Druckens, dh die Reihenfolge der Ausführung, wird geändert, und die Teilprozesse werden durch Start gestartet.Das Wesentliche ist, dass das Anwendungsprogramm eine Anfrage an das Betriebssystem initiiert Lassen Sie das Betriebssystem die Unterprozesse öffnen. Dies ist eine asynchrone Operation.Nachdem das Betriebssystem die Aufgabe erhalten hat, trifft es allgemeine Vorkehrungengemäß der Gesamtsituation, sodass die Startzeit jedes Teilprozesses nicht inOrdnung ist und die Endzeit nicht in Ordnung ist. Und der Hauptprozess wartet nicht darauf, dass der untergeordnete Prozess beendet wird, bevor er ausgeführt wird.

Join-Methode

Im obigen Code wird der Hauptprozess ausgeführt, ohne auf das Ende des Unterprozesses zu warten, und die Join-Methode ermöglicht es dem Hauptprozess, auf das Ende des Unterprozesses zu warten, bevor der Hauptprozess ausgeführt wird. Als folgender Code:

import time
import os
from multiprocessing import Process

# 子进程的任务
def func(name):
    print(f'{name} is start')
    time.sleep(2)
    print(f'{name} is end')

if __name__ == '__main__':
    # 创建子进程,target是子进程需要执行的任务,args是任务的参数,为元组形式
    p = Process(target=func, args=('zhuang', ))
    # 开启子进程
    p.start()
    # 等待子进程结束才往下运行
    p.join()
    # 执行当前文件就是主进程
    print(f'os获取进程号{os.getpid()},主进程结束')

# 运行结果如下
zhuang start
zhuang end
os获取进程号15816,主进程结束

Der Prozess des Hauptprozesses, der auf den untergeordneten Prozess wartet, wird ebenfalls in zwei Situationen unterteilt:

Die erste Methode: zuerst alle untergeordneten Prozesse starten und dann den Hauptprozess warten lassen

Wenn der Hauptprozess auf den untergeordneten Prozess wartet, bleibt der Hauptprozess hängen. Zu diesem Zeitpunkt wurden alle Unterprozesse zum Starten an das Betriebssystem übergeben, und die Unterprozesse werden normal ausgeführt.Nachdem alle Unterprozesse beendet sind, wird der Hauptprozess erneut ausgeführt. Zu diesem Zeitpunkt wird jeder untergeordnete Prozess gleichzeitig ausgeführt, sodass die Wartezeit des Hauptprozesses etwa 3 Sekunden beträgt, was die längste Laufzeit ist, die der untergeordnete Prozess benötigt, wie im folgenden Code gezeigt

import time
import os
from multiprocessing import Process

# 子进程的任务
def func(name, i):
    print(f'{name} start')
    time.sleep(i)
    print(f'{name} end')
    
if __name__ == '__main__':
    p_list = []
    for i in range(1, 4):
        p = Process(target=func, args=(f'p{i}', i))
        p_list.append(p)
        p.start()	# 先开启所有子进程
    for p in p_list:
        p.join()	# 再让主进程等待子进程
    print('主进程结束')
    
# 运行结果如下
p2 is start
p1 is start
p3 is start
p1 is done
p2 is done
p3 is done
主进程结束

Der zweite Typ, nachdem jeder Unterprozess gestartet wurde, muss er ausgeführt werden, bevor er ausgeführt werden kann

In diesem Fall wird die Ausführung des Programms seriell, und jeder Prozess muss warten, bis der vorherige Prozess seine Ausführung beendet hat, bevor er ausgeführt werden kann. Der folgende Code

import time
import os
from multiprocessing import Process

# 子进程的任务
def func(name, i):
    print(f'{name} start')
    time.sleep(i)
    print(f'{name} end')
    
if __name__ == '__main__':
    for i in range(1, 4):
        p = Process(target=func, args=(f'p{i}', i))
        p.start()	# 先开启所有子进程
        p.join()  # 一个一个运行子进程
    print('主进程结束')
    
# 运行结果,变成了串行
p1 is start
p1 is done
p2 is start
p2 is done
p3 is start
p3 is done
主进程结束

Speicherisolation zwischen Prozessen

Der Speicherplatz zwischen Prozessen ist voneinander isoliert, das heißt, die Daten zwischen Prozessen sind voneinander isoliert und beeinflussen sich nicht gegenseitig. Unterschiedliche Prozesse haben unterschiedliche Namensräume, nur der Wert des Namens in einem bestimmten Prozess-Namensraum wird geändert, und der Wert des Namens in anderen Prozess-Namensräumen wird nicht beeinflusst. Als folgender Code:

money = 100

def task():
    global money 
    money = 666
    print('子', money)	# 666


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()	# 主进程等待子进程p结束后再执行
    print('主', money)	# 100

Eigenschaften des Prozessobjekts

Ein Prozessobjekt ist Processein Objekt einer Klasse oder Processein von der Instanziierung einer Klasse geerbtes Objekt Dieses Prozessobjekt hat viele Datenattribute und Methoden, die wir verwenden können.

from multiprocessing import Process, current_process
import os

# 进程对象的数据属性和函数属性
p.start()		# 开启子进程
p.join()		# 主进程等待p进程结束
p.run()			# p进程具体执行的任务,可以重写这个方法

p.is_alive()	# 判断子进程是否存活,返回布尔值	
p.terminate()	# 结束子进程, 异步调用操作系统,所以不会立马关闭
p.kill()		# 结束子进程,同理
p.name			# 获取子进程的名字或修改之,注意自定义类时name属性的命名冲突问题,先super再赋值
p.daemon		# 判断子进程是否为守护进程,或者设置其为守护进程,p.daemon = True

# 补充:获取进程的pid
current_process().pid	# 当前进程pid
os.getpid()				# 当前进程pid
os.getppid()			# 当前进程的父进程的pid

Zombie-Prozess

Zombies können in Filmen auf der Grundlage des gesunden Menschenverstands interpretiert werden 死了但是没有死透的人.... Ein Hauptprozess erstellt einen Unterprozess. Wenn der Unterprozess beendet oder beendet wird, werden die vom Unterprozess belegten Ressourcen nicht sofort freigegeben, da der Unterprozess benötigt um dem Hauptprozess zu erlauben, den Unterprozess unter dem Hauptprozess zu sehen Einige grundlegende Informationen des geöffneten untergeordneten Prozesses, wie PID usw., so dass der untergeordnete Prozess zu einem Zombie-Prozess wird.

Das Ende des Kindprozesses und die Ausführung des Elternprozesses sind ein asynchroner Prozess, das heißt, der Elternprozess kann niemals vorhersagen, wann der Kindprozess enden wird; wenn der Kindprozess sofort alle seine Ressourcen zurückfordert, sobald er endet, dann Der übergeordnete Prozess kann die Informationen des untergeordneten Prozesses nicht abrufen.

Wenn der übergeordnete Prozess nicht endet und unbegrenzt untergeordnete Prozesse erstellt, ist der Zombie-Prozess in diesem Fall schädlich und beansprucht Systemressourcen, aber alle Prozesse sind nicht so gut wie der Zombie-Prozess.

verwaister Prozess

Waisen können auf der Grundlage des gesunden Menschenverstandes im täglichen Leben erklärt werden 没有父母的孩子: Wenn in einem Programm ein übergeordneter Prozess beendet wird, aber seine untergeordneten Prozesse noch ausgeführt werden, dann sind diese untergeordneten Prozesse, die noch ausgeführt werden, verwaiste Prozesse.

Das Betriebssystem wird eine spezielle Kinderhilfseinrichtung einrichten, init进程um verwaiste Prozesse zu verwalten und Ressourcen zu recyceln.

Daemon-Prozess

Der Hauptprozess legt einen untergeordneten Prozess als Daemon-Prozess fest, und der Daemon-Prozess endet zusammen mit dem Ende des Hauptprozesses Der untergeordnete Prozess kann nicht im Daemon-Prozess gestartet werden, und der untergeordnete Prozess muss als Daemon-Prozess festgelegt werden, bevor der gestartet wird Wenn der Daemon-Prozess die Ausführung des Hauptprozesses beendet Der Prozess läuft noch, hat also keine Auswirkungen auf den Daemon-Prozess, wie der alte Kaiser (Hauptprozess) und seine Konkubine (Daemon-Prozess), der Kaiser starb (der Hauptprozess endet), die Konkubine wird mit ihm begraben (der Dämonprozess wird beendet). Wenn die Konkubine (Daemonprozess) vor den Augen des Kaisers (Hauptprozess) stirbt, ist dies das Ende des Lebens. Code zeigen wie folgt:

from multiprocessing import Process
import time

def task(name):
    print(f'{name}娘娘开始侍候皇帝')
    time.sleep(3)
    print(f'{name}已经正常死亡')
    
if __name__ == '__main__':
    p = Process(target=task, kwargs={'name':'王后'})
    # 这句话一定要放在start上面,否则会报错
    p.daemon = True  # 将进程p设置成守护进程
    p.start()
    # time.sleep(5)  # 加上这句代码守护进程就会正常结束
    print('秦始皇寿终正寝!')

Prozesssynchronisation (Sperre)

Daten werden nicht zwischen Prozessen geteilt, aber sie teilen sich dasselbe Dateisystem, sodass der Zugriff auf dieselbe Datei kein Problem darstellt, aber wenn mehrere Prozesse gleichzeitig mit einem Datenelement arbeiten, kommt es zu einer Datenverwirrung.

Die Lösung für die oben genannten Probleme besteht darin, Sperren hinzuzufügen, um die Parallelität in eine serielle umzuwandeln.Obwohl die Effizienz geopfert wird, ist die Sicherheit der Daten garantiert.Verschiedene Teilprozesse greifen Sperren, und diejenigen, die die Sperren greifen, können den Code weiterhin ausführen muss warten, bis die letzte Person die Sperre freigibt, und darum kämpfen.

Die grundlegende Verwendung der Sperre ist wie folgt: Erstellen Sie eine Sperre im Hauptprozess und lassen Sie alle Unterprozesse sie zuerst greifen und zuerst verwenden. Wenn Sie sie nicht greifen können, warten Sie auf das nächste Mal in der Schlange.

from multiprocessing import Lock

mutex = Lock()
mutex.acquire()		# 抢锁
mutex.release()		# 释放锁,还锁

Nehmen wir das Ticket-Grabbing als Beispiel, um zu veranschaulichen, wie man Sperren verwendet:

from multiprocessing import Process, Lock
import json
import random
import time
class People:
    def __init__(self,name):
        self.name = name
    
    # 查询余票
    def query(self):
        with open('data','r',encoding='utf-8') as f:
            dic = json.load(f)
            num = dic.get('ticket_num')
        print(f'{self.name} 查询到余票是{num}')
        return dic
    # 购买票
    def buy(self):
        dic = self.query()
        time.sleep(random.randint(1,3))
        if dic.get('ticket_num') > 0:
            dic['ticket_num'] -= 1
            with open('data','w',encoding='utf-8') as f:
                json.dump(dic,f)
            print(f'{self.name}订票成功')
        else:
            print(f'{self.name}订票失败')
	
    # 整合上面两个方法
    def run(self, mutex): 
        # 先查余票
        self.query()
        # 子进程进行抢锁
        mutex.acquire()
        self.buy()
        # 购票操作完成后释放锁,由其他子进程继续争抢
        mutex.release()

if __name__ == '__main__':
    mutex = Lock()  # 获取锁
    for i in range(10):
        p = Process(target=People(i).run,args=(mutex,))
        p.start()

Angenommen, die Anzahl der im Wörterbuch gespeicherten Tickets ist 1, wenn es 10 Unterprozesse gibt, die jeden Prozess gleichzeitig abfragen, findet jeder Prozess 1 Ticket, dann kaufen diese 10 Unterprozesse gleichzeitig Tickets gekauft haben, was offensichtlich logisch falsch ist, aber dieses Problem kann durch Hinzufügen einer Sperre vermieden werden.

Verwenden Sie Sperren nicht auf die leichte Schulter, sondern sperren Sie nur, wenn mehrere Prozesse um Daten konkurrieren.

Warteschlange

Importieren Sie die Warteschlange über das Multiprocessing-Modul, verwenden Sie die Warteschlange oder importieren Sie sie direkt.  import queueDie Warteschlange basiert auf 管道+锁der Implementierung. Die Warteschlange sollte keine großen Daten speichern, und die Anzahl der Daten in der Warteschlange ist durch die Größe des Speichers begrenzt.

from multiprocessing import Queue

# 创建一个队列
q = Queue(5)  # 括号内可以传数字 标示生成的队列最大可以同时存放的数据量

q.put(111)
q.put(222)
q.put(333)
# print(q.full())  # 判断当前队列是否满了
# print(q.empty())  # 判断当前队列是否空了
q.put(444)
q.put(555)
# print(q.full())  # 判断当前队列是否满了

# q.put(666)  # 当队列数据放满了之后 如果还有数据要放程序会阻塞 直到有位置让出来 不会报错

"""
存取数据 存是为了更好的取
千方百计的存、简单快捷的取
"""

# 去队列中取数据
v1 = q.get()
v2 = q.get()
v3 = q.get()
v4 = q.get()
v5 = q.get()
# print(q.empty())
# V6 = q.get_nowait()  # 没有数据直接报错queue.Empty
# v6 = q.get(timeout=3)  # 没有数据之后原地等待三秒之后再报错  queue.Empty
try:
    v6 = q.get(timeout=3)
    print(v6)
except Exception as e:
    print('一滴都没有了!')

# # v6 = q.get()  # 队列中如果已经没有数据的话 get方法会原地阻塞
# print(v1, v2, v3, v4, v5, v6)

"""
q.full()
q.empty()
q.get_nowait()
在多进程的情况下是不精确
"""

IPC-Mechanismus für die Kommunikation zwischen Prozessen

Prozesskommunikation (IPC, Intent-Process Communication), dh Kommunikation zwischen Prozessen, einschließlich Hauptprozess-Unterprozess, Unterprozess-Unterprozess

Prozesse sind voneinander isoliert.Um Interprozesskommunikation (IPC) zu implementieren, unterstützt das Multiprocessing-Modulzwei Formen: 队列和管道, die beide auf dem Message-Passing-System basieren. Warteschlangen werden empfohlen, weil 队列是采用管道+锁 实现的.

from multiprocessing import Queue, Process

def producer(q):
    q.put('hello, 很高兴为您服务')


def consumer(q):
    print(q.get())


if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    q.put('start')  # 主进程传数据

Je suppose que tu aimes

Origine blog.csdn.net/NHB456789/article/details/130458556
conseillé
Classement