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()
posted @ 2017-11-30 17:45  _岩哥  阅读(157)  评论(0)    收藏  举报