进程的一点点理解

一、 进程

进程为计算机操作系统资源分配的基本单位,一般来说,开启一个进程,操作系统会分配一定的内存给予进程保证运行。

一般来说,计算机的核数大小决定了可以并发执行多少个进程。

1. 通过 multiprocessing 创建 Process 单例, 创建简单的进程from multiprocessing import Process

from multiprocessing import Process
import os
#
def run_proc(name):
    print('name的父进程是: {}'.format(os.getppid()))
    print ('Run child process %s (%s)...' % (name, os.getpid()))

if __name__ == '__main__':

    print("当前主进程:", os.getpid())
    process1 = Process(target=run_proc, args=('process1',))
    process1.start()
    process1.join()
    print("End")

"""
>>>
  当前主进程: 10232
  name的父进程是: 10232
  Run child process process1 (10968)...
  End
"""

  

2. 通过进程池进行进程数量控制

from multiprocessing import Pool
import time,random,os

def long_time_task(name):
    time.sleep(2)
    print("name的父进程是:", os.getppid())
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task {} Run %.2f seconds.'.format(name) %(end-start))

if __name__ == '__main__':

    print("当前主进程:", os.getpid())
    # Pool进程池表示在同一个时间,并发执行的最大的数量任务
    p = Pool(8)

    for i in range(9):
        p.apply_async(long_time_task, args=(i,))
    print("Waiting for all process finished...")
    p.close()
    p.join()
    print("Over")
    
"""
>>>
  当前主进程: 17304
  Waiting for all process finished...
  name的父进程是: 17304
  name的父进程是: 17304
  name的父进程是: 17304
  name的父进程是: 17304
  name的父进程是: 17304
  name的父进程是: 17304
  name的父进程是: 17304
  name的父进程是: 17304
  Task 0 Run 0.23 seconds.
  Task 6 Run 0.66 seconds.
  Task 7 Run 1.17 seconds.
  Task 4 Run 1.55 seconds.
  Task 5 Run 1.83 seconds.
  Task 1 Run 1.99 seconds.
  name的父进程是: 17304
  Task 2 Run 2.44 seconds.
  Task 3 Run 2.95 seconds.
  Task 8 Run 2.56 seconds.
  Over
"""

  

# Pool进程池表示在同一个时间,并发执行数量的任务;

由于本人电脑为8核,所以创建了9个进程对象,由此可见8个进程在并发执行任务。

 

3.Process的进程通信,借助multiprocessing底层的Queue进行测试

from multiprocessing import Queue, Process
import os,time,random


def write_data(q, name):
    print("Process {} to write: %s".format(name) % os.getpid())
    for i in ["A", "B", "C"]:
        print("----write %s----" % i)
        q.put(i)
        time.sleep(random.random() * 3)

def read_data(q,name):
    print("Process {} to read: %s".format(name) % os.getpid())
    while True:
        value = q.get(True)
        print("----read %s----" % value)

if __name__ == '__main__':

    q = Queue()

    process1 = Process(target=write_data, args=(q,"process1"))
    process2 = Process(target=read_data, args=(q,"process2"))

    process1.start()
    process2.start()

    process1.join()

    process2.terminate()

"""
>>>
Process process1 to write: 11848
----write A----
Process process2 to read: 5880
----read A----
----write B----
----read B----
----write C----
----read C----
"""

  

# 借助Queue类对象进行队列传递,写入在进行读出,实现Process之间的通信原理

 

二、 线程

 线程为计算机调度的基本单位,cpu调度的基本单位;线程也可以开启多任务,线程依赖于进程,一个进程可以创建多个线程;

线程中标量可以共享,需要注意资源竞争问题,通常来说解决方案有两种:

  1. 线程等待:threadx.join
  2. Lock锁:Lock = thread.lock() ; Lock.acquire()  --> Lock.release()
# 线程等待
def task(name):
    # sum = 0
    for i in range(5):
        global sum
        sum += i
        print("{}:{}".format(name, sum))
        # time.sleep(0)

if __name__ == '__main__':

    import threading

    thread1 = threading.Thread(target=task, args=("thread1",))
    thread2 = threading.Thread(target=task, args=("thread2",))

    thread1.start()
    thread1.join()

    thread2.start()


# 线程加锁
import time
import threading

lock = threading.Lock()
sum = 0

def task(name):
    # sum = 0
    lock.acquire()
    for i in range(5):
        global sum
        sum += i
        print("{}:{}".format(name, sum))
        # time.sleep(0)
    lock.release()

if __name__ == '__main__':

    thread1 = threading.Thread(target=task, args=("thread1",))
    thread2 = threading.Thread(target=task, args=("thread2",))

    thread1.start()
    thread2.start()

 

# 特别注意,python解释器中会默认给每个线程添加一把叫做GIL的锁,使得多线程执行的时候,其中一个执行小于100条或完成的时候,释放GIL锁,从而多线程交替执行。

 

计算密集型 vs. IO密集型

是否采用多任务的第二个考虑是任务的类型。我们可以把任务分为计算密集型和IO密集型。

计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。

计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。

第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。

IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。

 

 

三、分布式进程案例

 # task_master.py

import time, queue
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support

# 任务个数
task_number = 10
# 定义收发队列
task_queue = queue.Queue(task_number)
result_queue = queue.Queue(task_number)


def gettask():
    return task_queue


def getresult():
    return result_queue


def test():
    # windows下绑定调用接口不能使用lambda,所以只能先定义函数再绑定
    BaseManager.register('get_task', callable=gettask)
    BaseManager.register('get_result', callable=getresult)
    # 绑定端口并设置验证码,windows下需要填写ip地址,linux下不填默认为本地
    manager = BaseManager(address=('127.0.0.1', 5002), authkey=b'123')
    # 启动
    manager.start()
    try:
        # 通过网络获取任务队列和结果队列
        task = manager.get_task()
        result = manager.get_result()
        # 添加任务
        for i in range(task_number):
            print('Put task %d...' % i)
            task.put(i)
        # 每秒检测一次是否所有任务都被执行完
        while not result.full():
            time.sleep(1)
        for i in range(result.qsize()):
            ans = result.get()
            print('task %d is finish , runtime:%d s' % ans)

    except:
        print('Manager error')
    finally:
        # 一定要关闭,否则会爆管道未关闭的错误
        manager.shutdown()


if __name__ == '__main__':
    # windows下多进程可能会炸,添加这句可以缓解
    freeze_support()
    test()

  

# task_worker.py

import time, sys, queue, random
from multiprocessing.managers import BaseManager
BaseManager.register('get_task')
BaseManager.register('get_result')
conn = BaseManager(address = ('127.0.0.1',5002), authkey = b'123')
try:
    conn.connect()
except:
    print('连接失败')
    sys.exit()
task = conn.get_task()
result = conn.get_result()
while not task.empty():
    n = task.get(timeout = 1)
    print('run task %d' % n)
    sleeptime = random.randint(0,3)
    time.sleep(sleeptime)
    rt = (n, sleeptime)
    result.put(rt)

  

# 运行结果:

 

# Queue对象存储在哪?注意到task_worker.py中根本没有创建Queue的代码,所以,Queue对象存储在task_master.py进程中:

 

 

 

 

 

 

......待补充学习

 

posted @ 2019-06-20 20:35  Harp_Yestar  阅读(196)  评论(0)    收藏  举报