Python 中的多进程和多线程

小编 2026-06-14 阅读:144 评论:0
Python 中的多进程和多线程 多进程和多线程是实现多任务及提升任务执行效率的两种常用方法。两者都遵循 Master-Worker 模式,Master 负责分配任务,Worker 负责执行任...

Python 中的多进程和多线程

多进程多线程是实现多任务及提升任务执行效率的两种常用方法。两者都遵循 Master-Worker 模式,Master 负责分配任务,Worker 负责执行任务。具体来说,多进程由一个主进程与多个子进程构成,而多线程由一个主线程与多个子线程构成。两者的一个主要区别在于:多进程的每个进程创建在不同的内存空间中,而多线程创建在一个进程中。

一、 多进程与多线程的优缺点对比:

  • 多进程的优点:稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程(当然主进程崩溃了所有进程就全崩溃了,但是主进程只负责分配任务,崩溃的概率很低)。多进程模式的另一个优点是可以将进程分布到多台机器上。
  • 多进程的缺点:创建进程的代价大,一般情况下创建的进程数不超过CPU核数的2倍。
  • 多线程的优点:系统开销较小,在Windows下,多线程的效率比多进程要高
  • 多线程的缺点:稳定性差,由于多线程创建在一个进程中,这就意味着多个线程共用相同的内存空间,所以当一个线程崩溃,其它线程也会崩溃。此外,多线程不能分布在多台机器上。
  • Python解释器由于设计时有GIL全局锁,导致无法利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

二、 多进程的实现

  1. 通过调用 multiprocessing 模块的 Process 可以创建一个进程对象,创建进程之后,操作系统会自动把当前进程(称为父进程)复制一份(称为子进程),然后,分别在父进程和子进程内返回,子进程永远返回0,而父进程返回子进程的ID。
from multiprocessing import Process
import os

def run_proc(name):
	# getpid() 和 getppid() 分别用于获取当前进程及当前进程父进程的 ID
    print(\'Run child process %s (%s)...\' % (name, os.getpid()))
    print(\'Run parent process %s (%s)...\' % (name, os.getppid()))

if __name__ == \'__main__\':
	print(\'Parent process %s.\' % os.getpid())
	p = Process(target = run_proc, args = (\'test\',))
	p.start()  # 进程开始
	p.join()   # 等待结束
	print(\'End\')
  1. 通过调用 Pool() 模块来创建多个进程
from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print(\'Run task %s (%s)...\' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print(\'Task %s runs %0.2f seconds.\' % (name, (end - start)))
    
if __name__==\'__main__\':
    print(\'Parent process %s.\' % os.getpid())
    p = Pool(4)    # 进程池的个数一般取CPU个数的1到2倍
    for i in range(8):
        p.apply_async(long_time_task, args=(i,))
    print(\'Waiting for all subprocesses done...\')
    p.close()  # close 后不能再添加进程
    p.join()
    print(\'All subprocesses done.\')
  1. 进程间共享变量
from multiprocessing import Process, Value, Array  #引入多进程版本的value和array
def f(n, a):
    n.value = 3.1415927
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == \'__main__\':
    num = Value(\'d\', 0.0)
    arr = Array(\'i\', range(10))
    p = Process(target=f, args=(num, arr))
    p.start()
    p.join()
    print(num.value)
    print(arr[:])
  1. 通过在进程间引入 Queue(队列)来实现进程间通信
from multiprocessing import Process, Queue
import os, time, random

def write(q):
    print(\'Process to write: %s\' % os.getpid())
    for value in [\'A\', \'B\', \'C\']:
        print(\'Put %s to queue...\' % value)
        q.put(value)
        time.sleep(random.random())
def read(q):
    print(\'Process to read: %s\' % os.getpid())
    while True:
        value = q.get(True)
        print(\'Get %s from queue.\' % value)

if __name__==\'__main__\':
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pr.start()
    pw.join()
    pr.terminate()  # pr 是死循环,需要强行停止
  1. 创建并控制子进程
import subprocess

# 下面的代码相当于直接执行命令`nslookup www.python.org`
r = subprocess.call([\'nslookup\', \'www.python.org\'])
print(\'Exit code:\', r)

# 如果想要输入,需要调用 communicate()
p = subprocess.Popen([\'nslookup\'], 
                     stdin=subprocess.PIPE,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.PIPE)
output, err = p.communicate(b\'set q=mx\\npython.org\\nexit\\n\')
print(output)
print(\'Exit code:\', p.returncode)

三、多线程的实现

  1. 每调用一次 threading.Thread() 就可以创建一个线程
def loop():
    thread_name = threading.current_thread().name  # 获取当前线程的名称
    print(\'Thread %s is running...\' % thread_name)
    n = 0
    while n < 5:
        n = n + 1
        print(\'Thread %s >>> %d\' % (thread_name, n))
    print(\'Thread %s ends.\' % thread_name)
    
thread_name = threading.current_thread().name
print(\'Thread %s is running...\' % thread_name)
t1 = threading.Thread(target = loop, name=\'LoopThread-1\')  # 为线程命名,t1执行完之后才会执行t2
t2 = threading.Thread(target = loop, name=\'LoopThread-2\') 
t1.start()
t2.start()
t1.join()
t2.join()
print(\'Thread %s ends.\' % thread_name)
  1. 在多线程中添加线程锁。多进程中,同一个变量各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改。如何各线程正常执行一般不会有什么问题,但是如果某个线程意外中断,就会导致变量被赋予意想不到的数值,为了防止这种情况发生就需要添加线程锁。线程锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,比如它阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。
import time, threading

local_school = threading.local() # 定义threadinglocal对象

def process_student(std):
    std = local_school.student  # 获取当前线程的局部变量
    print(\'Hello %s (%s)\\n\' % (std, threading.current_thread().name))

def process_thread(name):
    local_school.student = name # 将name传入threadinglocal对象
    process_student(name)

t1 = threading.Thread(target = process_thread, args = (\'Tom\', ), name = \'TA\')
t2 = threading.Thread(target = process_thread, args = (\'Jack\', ), name = \'TB\')
print(t1.name)
print(t2.name)
t1.start()
t2.start()
t1.join()
t2.join()

ThreadLocal 最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表