Python-多任务-进程

Python-多任务-进程

一 什么是进程

进程:是资源分配的基本单位。当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。CPU是以时间片的方式为进程分配CUP处理时间。
而一个进程又是由多个线程所组成的。

线程:是程序执行流的最小单元,是系统独立调度和分配CPU(独立运行)的基本单位。线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。线程是进程中的最小的执行单位,进程想要执行任务就需要依赖线程,并且一个进程中至少有一个线程。

程序:例如xxx.py这是程序,是一个静态的

二 进程的过程

1 进程的状态

  • 就绪态:运行的条件都已经满足,正在等在cpu执行
  • 执行态:cpu正在执行其功能
  • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态

三 进程的特点/优缺点

1 进程和线程的对比

定义的不同

  • 进程是系统进行资源分配和调度的一个独立单位.
  • 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

区别

  • 一个程序至少有一个进程,一个进程至少有一个线程.
  • 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率

2 优缺点

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。

四 Python使用多进程

1 多进程简单使用

from multiprocessing import Process
import time


def process1():
    for i in range(10):
        time.sleep(1)

        print("process%s1:" % ("-"*5), i)
    print("process%s1Done" % ("-" * 5))

if __name__ == '__main__':
    p = Process(target=process1)
    p.start()

    for i in range(10):
        time.sleep(1)

        print("process%smain:" % ("-"*5), i)
    print("process%smainDone" % ("-" * 5))

2 获取进程id

from multiprocessing import Process
import time
import os


def process1():
    for i in range(10):
        time.sleep(1)

        print("process%s1:" % ("-"*5), i, [os.getpid()])  # 使用os模块获取进程id
    print("process%s1Done" % ("-" * 5))

if __name__ == '__main__':
    p = Process(target=process1)
    p.start()

    for i in range(10):
        time.sleep(1)

        print("process%smain:" % ("-"*5),  i, [os.getpid()])  # 使用os模块获取进程id
    print("process%smainDone" % ("-" * 5))

3 Process语法结构及常用方法

Process语法结构:

Process([group [, target [, name [, args [, kwargs]]]]])

  • target:如果传递了函数的引用,这个子进程的任务就是执行这里的代码
  • args:给target指定的函数传递的参数,以元组的方式传递
  • kwargs:给target指定的函数传递命名参数
  • name:给进程设定一个名字,可以不设定
  • group:指定进程组,大多数情况下用不到

Process创建的实例对象的常用方法:

  • start():启动子进程实例(创建子进程)
  • is_alive():判断进程子进程是否还在活着
  • join([timeout]):是否等待子进程执行结束,或等待多少秒
  • daemon:守护进程,默认为False;设置为True后(要在start()前设置),主进程完成后,不管子进程走到哪里,都要停止并退出
  • terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性:

  • name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
  • pid:当前进程的pid(进程号)
from multiprocessing import Process
import time
import os


def process1():
    for i in range(10):
        time.sleep(1)

        print("process%s1:" % ("-"*5), i, [os.getpid()])
    print("process%s1Done" % ("-" * 5))


if __name__ == '__main__':
    p = Process(target=process1)
    p.start()
    print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
    print("子进程是否存活:%s" % p.is_alive())  # 子进程是否存活
    # p.join()  # 在这里阻塞,直到子进程执行完成,可以设置时间

    p.terminate()  # 不管任务是否完成,立即终止子进程,但是结束的时候需要一点时间,不然p.is_alive()还是True

    time.sleep(1)
    print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
    print("子进程是否存活:%s" % p.is_alive())  # 子进程是否存活

    # for i in range(10):
    #     time.sleep(1)
    #
    #     print("process%smain:" % ("-"*5),  i, [os.getpid()])
    print("process%smainDone" % ("-" * 5))

4 给子进程指定的函数传递参数

from multiprocessing import Process
import time
import os


def process1(name, age, **kwargs):
    for i in range(10):
        time.sleep(1)

        print("process%s1:" % ("-"*5), i, [os.getpid()])
        print(name, age, kwargs)
    print("process%s1Done" % ("-" * 5))


if __name__ == '__main__':
    p = Process(target=process1, args=("chen", 18), kwargs={"key1": "value1", "key2": "value2"})
    p.start()
    print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
    print("子进程是否存活:%s" % p.is_alive())  # 子进程是否存活
    # p.join()  # 在这里阻塞,直到子进程执行完成,可以设置时间

    # p.terminate()  # 不管任务是否完成,立即终止子进程,但是结束的时候需要一点时间,不然p.is_alive()还是True

    time.sleep(1)
    print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
    print("子进程是否存活:%s" % p.is_alive())  # 子进程是否存活

    # for i in range(10):
    #     time.sleep(1)
    #
    #     print("process%smain:" % ("-"*5),  i, [os.getpid()])
    print("process%smainDone" % ("-" * 5))

5 进程间不共享全局变量

from multiprocessing import Process
import time
import os


test_list = [1, 2, 3, 4, 5]


def process1(name, test_list):
    test_list.append(name)
    print(id(test_list), name, ":", test_list)
    print("process%s1Done" % ("-" * 5))


if __name__ == '__main__':
    p1 = Process(target=process1, args=("chen", test_list))
    p1.start()
    p2 = Process(target=process1, args=("jun", test_list))
    p2.start()
    p3 = Process(target=process1, args=("ming", test_list))
    p3.start()

    p1.join()
    p2.join()
    p3.join()

    print(id(test_list), "main:", test_list)

    print("process%smainDone" % ("-" * 5))

"""
44719880 chen : [1, 2, 3, 4, 5, 'chen']
process-----1Done
44916488 jun : [1, 2, 3, 4, 5, 'jun']
process-----1Done
44392200 ming : [1, 2, 3, 4, 5, 'ming']
process-----1Done
44107272 main: [1, 2, 3, 4, 5]
process-----mainDone
"""

线程对比

from threading import Thread
import time
import os


test_list = [1, 2, 3, 4, 5]


def process1(name, test_list):
    test_list.append(name)
    print(id(test_list), name, ":", test_list)
    print("process%s1Done" % ("-" * 5))


if __name__ == '__main__':
    p1 = Thread(target=process1, args=("chen", test_list))
    p1.start()
    p2 = Thread(target=process1, args=("jun", test_list))
    p2.start()
    p3 = Thread(target=process1, args=("ming", test_list))
    p3.start()

    p1.join()
    p2.join()
    p3.join()

    print(id(test_list), "main:", test_list)

    print("process%smainDone" % ("-" * 5))
    
"""
44006600 chen : [1, 2, 3, 4, 5, 'chen']
process-----1Done
44006600 jun : [1, 2, 3, 4, 5, 'chen', 'jun']
process-----1Done
44006600 ming : [1, 2, 3, 4, 5, 'chen', 'jun', 'ming']
process-----1Done
44006600 main: [1, 2, 3, 4, 5, 'chen', 'jun', 'ming']
process-----mainDone
"""

另一个例子

import threading
import time

from multiprocessing import Process
g_num = 20


def work1(num):
    global g_num
    for i in range(num):
        g_num += 1
    print("----in work1, g_num is %d---"%g_num)


def work2(num):
    global g_num
    for i in range(num):
        g_num += 1
    print("----in work2, g_num is %d---"%g_num)


if __name__ == '__main__':  #多进程需要在main函数中运行

    print("---进程创建之前g_num is %d---"%g_num)

    t1 = Process(target=work1, args=(1000000,))
    t1.start()

    t2 = Process(target=work2, args=(1000000,))
    t2.start()

    time.sleep(5)

    print("2个进程对同一个全局变量操作之后的最终结果是:%s" % g_num)
    
"""
---进程创建之前g_num is 20---
----in work1, g_num is 1000020---
----in work2, g_num is 1000020---
2个进程对同一个全局变量操作之后的最终结果是:20
"""

6 进程间通信——Queue

初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);

  • Queue.qsize():返回当前队列包含的消息数量;

  • Queue.empty():如果队列为空,返回True,反之False ;

  • Queue.full():如果队列满了,返回True,反之False;

  • Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;

    • 1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;
    • 2)如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常;
  • Queue.get_nowait():相当Queue.get(False);

  • Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True;

    • 1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
    • 2)如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常;
  • Queue.put_nowait(item):相当Queue.put(item, False);

import time

from multiprocessing import Process
from multiprocessing import Queue

g_num = 20
# g_list = [1, 2, 3, 4]

q = Queue(3)  # 初始化一个Queue对象,最多可接收4条put消息


def work1(q, num):
    g_num = 0

    for i in range(num):

        g_num = q.get()
        g_num += 1
        q.put(g_num)
    print("----in work1, g_num is %d---"%g_num)


def work2(q, num):
    g_num = 0

    for i in range(num):
        g_num = q.get()
        g_num += 1
        q.put(g_num)
    print("----in work2, g_num is %d---"%g_num)


if __name__ == '__main__':  #多进程需要在main函数中运行

    print("---进程创建之前g_num is %d---"%g_num)

    q.put(g_num)

    t1 = Process(target=work1, args=(q, 100000))
    t1.start()

    t2 = Process(target=work2, args=(q, 100000))
    t2.start()

    while t1.is_alive() or t2.is_alive():
        pass


    g_num = q.get()

    print("2个进程对同一个全局变量操作之后的最终结果是:%s" % g_num)
    
    
"""
---进程创建之前g_num is 20---
----in work2, g_num is 200002---
----in work1, g_num is 200020---
2个进程对同一个全局变量操作之后的最终结果是:200020
"""

7 进程池——Pool

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务

multiprocessing.Pool常用函数解析:

  • apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
  • close():关闭Pool,使其不再接受新的任务;
  • terminate():不管任务是否完成,立即终止;其中close()跟terminate()的区别在于close()会等待池中的worker进程执行结束再关闭pool,而terminate()则是直接关闭
  • join():主进程阻塞,等待子进程的退出, 必须在close()或terminate()之后使用;

注意:如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:

RuntimeError: Queue objects should only be shared between processes through inheritance.

import time

# from multiprocessing import Process
# from multiprocessing import Queue
from multiprocessing import Pool, Manager

g_num = 20
# g_list = [1, 2, 3, 4]


def work(q, num, time_num):
    start_time = time.time()
    g_num = 0

    for i in range(num):

        g_num = q.get()
        g_num += 1
        q.put(g_num)
    end_time = time.time()
    print("----in work%d, g_num is %d---耗时:%0.3f" % (time_num, g_num, end_time-start_time))


if __name__ == '__main__':  # 多进程需要在main函数中运行
    q = Manager().Queue(3)  # 初始化一个Queue对象,最多可接收3条put消息
    print("---进程创建之前g_num is %d---"%g_num)

    q.put(g_num, False)
    pool = Pool(3)

    for i in range(10):
        result = pool.apply_async(work, (q, 1000, i))
        print(result)

    print(pool._cache)  # 查看进程的数量

    # pool.terminate()  # 其中close()跟terminate()的区别在于close()会等待池中的worker进程执行结束再关闭pool,而terminate()则是直接关闭
    pool.close()

    pool.join()  # 主进程阻塞,等待子进程的退出, 必须在close()或terminate()之后使用
    print(result.successful())  # 没有完成会抛出异常。完成为True

    print("-"*150)

    print(pool._cache)  # 查看进程的数量
    while len(pool._cache) > 0:
        pass
    g_num = q.get()

    print("所有进程对同一个全局变量操作之后的最终结果是:%s" % g_num)
posted @   陈俊明  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示