进程的一点点理解
一、 进程
进程为计算机操作系统资源分配的基本单位,一般来说,开启一个进程,操作系统会分配一定的内存给予进程保证运行。
一般来说,计算机的核数大小决定了可以并发执行多少个进程。
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调度的基本单位;线程也可以开启多任务,线程依赖于进程,一个进程可以创建多个线程;
线程中标量可以共享,需要注意资源竞争问题,通常来说解决方案有两种:
- 线程等待:threadx.join
- 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进程中:

......待补充学习

浙公网安备 33010602011771号