day32---并发编程之多进程
1. 进程
-
描述:正在进行和执行的一个过程或任务;进程是一个抽象的概念,起源于操作系统。进程的创建、调用管理都归操作系统管
2. 开启子进程的两种方式
-
方式一:
from multiprocessing import Process
def task(name):
print('name is %s' % name)
if __name__ == '__main__':
p = Process(target=task, args=('yy',), name='yy_name')
p.start()
print('进程名是:%s' % p.name)
print('进程pid是:%s' % p.pid)
print('岩哥...')
>>>
进程名是:yy_name
进程pid是:8560
岩哥...
name is yy
-
方式二:
import os
from multiprocessing import Process
class Task(Process):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print('name is %s' % self.name)
if __name__ == '__main__':
p = Task('yan')
p.start()
print('进程pid是:%s' % os.getpid())
print('岩哥...')
>>>
进程pid是:9624
岩哥...
name is yan
3. multiprocessing模块中的Process
-
target:表示调用对象
-
args:表示调用对象的位置参数元组
-
kwargs:表示调用对象的字典
-
name:表示子进程的进程名
-
name():获取进程名
-
pid():获取进程pid
-
start():启动进程
-
run():进程启动时调用的方法,等同于target指定的函数名,不推荐使用
-
join():等待子进程运行完直到终止,join只能等待start()开启的进程,无法等待run()开启的
-
is_alive():判断进程是否处于运行状态,是返回True,否则返回False
-
termenate():终止进程,不会进行任何清理操作,不推荐使用
# 给操作系统发送一个信号要启动一个进程,等待运行完再发送信号启动另外一个,直到信号发送完和运行完之后才停止,消耗的总时间是所有进程运行时间的总和
import time
import random
from multiprocessing import Process
def task1():
print('111-starting...')
random_time = random.randint(1, 3)
print('task1将要睡眠%s秒种' % random_time)
time.sleep(random_time)
print('111-stoping...')
def task2():
print('222-starting...')
random_time = random.randint(1, 3)
print('task2将要睡眠%s秒种' % random_time)
time.sleep(random_time)
print('222-stoping...')
def task3():
print('333-starting...')
random_time = random.randint(1, 3)
print('task3将要睡眠%s秒种' % random_time)
time.sleep(random_time)
print('333-stoping...')
if __name__ == '__main__':
p1 = Process(target=task1)
p2 = Process(target=task2)
p3 = Process(target=task3)
p_list = [p1, p2, p3]
start_time = time.time()
for i in p_list:
i.start()
i.join()
stop_time = time.time()
print('运行时间是:%s' % (stop_time - start_time))
>>>
111-starting...
task1将要睡眠3秒种
111-stoping...
222-starting...
task2将要睡眠3秒种
222-stoping...
333-starting...
task3将要睡眠1秒种
333-stoping...
运行时间是:7.564958572387695
# 同时给操作系统发送多个信号启动多个进程,然后直接等待程序运行完后停止,消耗的总时间是耗时最长的进程的时间
import time
import random
from multiprocessing import Process
def task1():
print('111-starting...')
random_time = random.randint(1, 3)
print('task1将要睡眠%s秒种' % random_time)
time.sleep(random_time)
print('111-stoping...')
def task2():
print('222-starting...')
random_time = random.randint(1, 3)
print('task2将要睡眠%s秒种' % random_time)
time.sleep(random_time)
print('222-stoping...')
def task3():
print('333-starting...')
random_time = random.randint(1, 3)
print('task3将要睡眠%s秒种' % random_time)
time.sleep(random_time)
print('333-stoping...')
if __name__ == '__main__':
p1 = Process(target=task1)
p2 = Process(target=task2)
p3 = Process(target=task3)
p_list = [p1, p2, p3]
start_time = time.time()
for i in p_list:
i.start()
for i in p_list:
i.join()
stop_time = time.time()
print('运行时间是:%s' % (stop_time - start_time))
>>>
111-starting...
task1将要睡眠2秒种
222-starting...
task2将要睡眠3秒种
333-starting...
task3将要睡眠2秒种
111-stoping...
333-stoping...
222-stoping...
运行时间是:3.297632932662964
import time
from multiprocessing import Process
def task(name, sex, age):
time.sleep(1)
print('name-->%s' % name)
time.sleep(0.5)
print('sex-->%s' % sex)
time.sleep(0.5)
print('age-->%s' % age)
time.sleep(1)
if __name__ == '__main__':
p = Process(target=task, args=('yan',), kwargs={'sex': 'man', 'age': 18}, name='yan_task')
p.start()
print('岩哥...')
print('进程名是:[%s]' % p.name)
print('进程的pid是:[%s]' % p.pid)
print('进程的状态:[%s]' % p.is_alive())
p.join()
print('进程的状态:[%s]' % p.is_alive())
print('岩哥...')
>>>
岩哥...
进程名是:[yan_task]
进程的pid是:[5760]
进程的状态:[True]
name-->yan
sex-->man
age-->18
进程的状态:[False]
岩哥...
4. 进程池
-
描述:在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。如果是硬件性能比较好的情况下,使用multiprocessing中的Process动态成生多个进程是很好的办法,但是随着客户端的数量增多,一直启动进程效率反而会下降。多进程是实现并发的手段之一,需要注意的问题是:
-
需要并发执行的任务通常要远大于核数
-
一个操作系统不可能无限开启进程,通常有几个核就开几个进程
-
进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)
-
-
优点:通过维护一个进程池来控制进程数目,根据系统和硬件的标准限制最小进程数和最大进程数
-
工作模式:对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程
-
提交/调用任务的方式有两种:
-
同步调用:提交或调用一个任务,会一直等待一个任务执行完成之后拿到了结果,才会执行下一个任务
-
# 同步调用:调用一次,等待运行结束得到返回结果,再进行下一次调用,永远不会达到进程池限制的最大进程数
import os
import time
from concurrent.futures import ProcessPoolExecutor
def task(name, n):
print('name是:%s, pid是:%s' % (name, os.getpid()))
time.sleep(1)
return n ** 2
if __name__ == '__main__':
p = ProcessPoolExecutor(4)
start_time = time.time()
for i in range(10):
res = p.submit(task, 'yan[%s]' % i, i)
print(res.result())
p.shutdown(wait=True)
stop_time = time.time()
print('运行时间是:%s' % (stop_time - start_time))
>>>
name是:yan[0], pid是:9448
0
name是:yan[1], pid是:4196
1
name是:yan[2], pid是:11124
4
name是:yan[3], pid是:10664
9
name是:yan[4], pid是:9448
16
name是:yan[5], pid是:4196
25
name是:yan[6], pid是:11124
36
name是:yan[7], pid是:10664
49
name是:yan[8], pid是:9448
64
name是:yan[9], pid是:4196
81
运行时间是:10.371966123580933
+ ##### 异步调用:提交或调用一个任务,不会等待当前任何的执行结果,会一直执行下一个任务,直到所有任务执行完之后一块拿到所有的结果
# 异步调用:进程池限制上限的进程数同时运行,有执行完的其他任务会立即运行,直到所有任务运行完,运行期间会一直有进程池限制的最大上限的进程数在同时工作
import os
import time
from concurrent.futures import ProcessPoolExecutor
def task(name, n):
print('name是:%s, pid是:%s' % (name, os.getpid()))
time.sleep(1)
return n ** 2
if __name__ == '__main__':
p = ProcessPoolExecutor(4)
start_time = time.time()
res_list = []
for i in range(10):
res = p.submit(task, 'yan[%s]' % i, i)
res_list.append(res)
for i in res_list:
print(i.result())
p.shutdown(wait=True)
stop_time = time.time()
print('运行时间是:%s' % (stop_time - start_time))
>>>
name是:yan[0], pid是:1940
name是:yan[1], pid是:9160
name是:yan[2], pid是:3692
name是:yan[3], pid是:4904
name是:yan[4], pid是:1940
0
name是:yan[5], pid是:9160
1
name是:yan[6], pid是:3692
4
name是:yan[7], pid是:4904
9
name是:yan[8], pid是:1940
16
name是:yan[9], pid是:9160
25
36
49
64
81
运行时间是:3.379833936691284
5. 应用
使用进程池的方式实现TCP协议的socket并发
# 服务端
import subprocess
import json
import struct
from socket import *
from concurrent.futures import ProcessPoolExecutor
def server_socket(ip, port):
server = socket(AF_INET, SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind((ip, port))
server.listen(5)
while True:
conn, add = server.accept()
p = ProcessPoolExecutor(4)
print('客户端[%s]成功连接,端口是[%s]' % (add[0], add[1]))
p.submit(shell_result, conn)
server.close()
def shell_result(conn):
while True:
try:
shell = conn.recv(1024)
if not shell:
break
obj = subprocess.Popen(shell.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
header_dict = {'total_size': len(stdout) + len(stderr)}
header_json = json.dumps(header_dict)
header_bytes = header_json.encode('utf-8')
header_size = struct.pack('i', len(header_bytes))
conn.send(header_size)
conn.send(header_bytes)
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
if __name__ == '__main__':
ip = '127.0.0.1'
port = 8080
server_socket(ip, port)
# 客户端1
import json
import struct
from socket import *
ip = '127.0.0.1'
port = 8080
client = socket(AF_INET, SOCK_STREAM)
client.connect((ip, port))
def shell_run(shell):
client.send(shell.encode('utf-8'))
header_len = client.recv(4)
header_size = struct.unpack('i', header_len)[0]
header_bytes = client.recv(header_size)
header_json = header_bytes.decode('utf-8')
header_dict = json.loads(header_json)
total_size = header_dict['total_size']
recv_size = 0
data = b''
while recv_size < total_size:
recv_data = client.recv(1024)
data += recv_data
recv_size += len(recv_data)
print(data.decode('gbk'))
if __name__ == '__main__':
while True:
shell = input('请输入需要执行的系统命令:').strip()
if not shell:
continue
shell_run(shell)
client.close()
# 客户端2
import json
import struct
from socket import *
ip = '127.0.0.1'
port = 8080
client = socket(AF_INET, SOCK_STREAM)
client.connect((ip, port))
def shell_run(shell):
client.send(shell.encode('utf-8'))
header_len = client.recv(4)
header_size = struct.unpack('i', header_len)[0]
header_bytes = client.recv(header_size)
header_json = header_bytes.decode('utf-8')
header_dict = json.loads(header_json)
total_size = header_dict['total_size']
recv_size = 0
data = b''
while recv_size < total_size:
recv_data = client.recv(1024)
data += recv_data
recv_size += len(recv_data)
print(data.decode('gbk'))
if __name__ == '__main__':
while True:
shell = input('请输入需要执行的系统命令:').strip()
if not shell:
continue
shell_run(shell)
client.close()

浙公网安备 33010602011771号