python多线程与多进程的方法

进程与线程的关系

一个进程有多个线程,进程之间变量不会共享,线程之间变量是可以共享的,往往需要加锁。进程都是在一个CPU核心里进行的,不会调用CPU的全部资源。

往往有几个CPU核心就可以创建多少个进程(当然也可以超过CPU核心数),每个进程单独占用一个CPU核心,所以多进程可以调用CPU的全部计算资源。

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

对应比较小的计算问题使用多线程是首选的,因为初始化快速。

并行计算并不是开启的线程或进程越多越好,这受到CPU核心的制约,一般建议开启的进程数不超过CPU核心数的2倍,因为过多的话很多时间就会浪费在操作系统协调CPU资源上了。

这里有一篇综述的文章:
https://blog.csdn.net/qq_36387683/article/details/81147505

多线程的使用方法

获取当前线程

import threading
#返回当前线程
t=threading.current_thread()
print(t)
#获得这个线程的名字
t.getName()
#判断线程是否存活
t.is_alive()

创建线程

import threading
#创建一个线程
my_thread=threading.Thread()
#创建一个名称为my_tread的线程
my_thread=threading.Thread(name='my_tread')

def print_i(i):
    t=threading.current_thread()
    print('线程:{};任务:{}'.format(t.getName(),i))
my_thread=threading.Thread(name='my_tread',target=print_i,args=(1,))
#启动线程
my_thread.start() # 打印 i:1

进程加锁的方法

import threading
import time
#创建进程锁的方法
lock=threading.Lock()
a=0
def add1():
    global a
    try:
        lock.acquire()#获得锁
        tmp=a+1
        time.sleep(0.2)#延时0.2秒,模拟写入所需时间
        a=tmp
    finally:
        lock.release()#释放锁
        print('进程:{};a:{}'.format(threading.current_thread().getName(),a))

threads=[threading.Thread(name='t{}'.format(i),target=add1) for i in range(10)]

[t.start() for t in threads]

利用Thread的派生子类实现多线程的并行计算

from threading import Thread
from queue import Queue

#创建子类
class worker(Thread):
    def __init__(self, work_queue):
        Thread.__init__(self)
        self.work_queue =work_queue

    def run(self):
        while self.work_queue.empty() == False:
           main_fun(self.html_queue.get())


if __name__ == '__main__':
    #创建任务队列
    mission_queue = Queue()
    mission_queue.put(work)

    # 创建n个线程类,并开启
    for i in range(n):
        workrobot = worker(mission_queue)
        workrobot.start()
        Workers.append(workrobot)

    for worker in Workers:
        worker.join()

多进程的使用方法

创建进程

from multiprocessing import Process
#创建一个进程
t=Process(name='my',target=fun,args=())
#t进程的名字
t.name
#判断t进程是否在工作
t.is_alive()
#开启t进程 
t.start()
# 阻塞进程
t.join()

利用派生Process的子类开启进程的方法

from multiprocessing import Process
 
class MyProcess(Process):
    def __init__(self):
        Process.__init__(self)
 
    def run(self):#run方法里写主要工作
        #这里写程序要实现的代码
 
def main():
    myp=MyProcess()
    myp.start()
    myp.join()
    print("主进程终止")
 
if __name__ == '__main__':
    main()

原文链接:https://blog.csdn.net/topleeyap/article/details/78981848

使用进程池Pool进行并行计算

  • 此处不赘述关于进程操作的复杂内容,请读者自行查阅有关计算机操作系统的资料,在此仅介绍有关针对计算任务量巨大并可将进程放入进程池 (即Poo1类)中的数据分析情形。

  • 当使用的操作对象数目不多时,还是可以直接使用Process类动态的生成多个进程的但是开启进程是要耗费系统资源的,通常CPU有几个核就开几个进程。如果进程数太多,手动限制进程数量就显得特别的繁琐,而且有些时候需要并行的任务数要远大于核数,此时进程池就能派上用场。

  • Poo1类可以提供指定数量的进程供用户调用。当有新的请求提交到Poo1中时,如果池没满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。

  • 所以这种并行操作可以节约大量的时间。可以使用如下方式创建Pool类的实例对象:
    Pool([processes=None[,initializer=Nonel,initargs=()[,maxtasksperchild=None]]]])

  • 其中可供选择的参数作用如下:

    • processes:要创建的进程数,如省略,将默认使用os.cpu_count() 的值:
    • initlalizer:每个工作进程启动时要执行的可调用对象,默认为None:
    • initarga;要传给initializer的参数组:
    • maxtaskaperchild:工作进程在退出之前可完成的任务数,完成后用一个新的工作进程来替代原进程,以使未使用的资源被释放。
  • Pool类具有如下常用的方法:

    • apply:在一个池工作进程中执行func(arge,** kwargs),然后返回结果。主进程会被阻塞直到函数执行结束;
    • apply_async:异步apply,与apply用法一样,但它是非阻塞且支持结果返回进行回调,更适合并行执行工作。其func函数仅被poo1中的一个程调用:
    • map:与内置map函数用法行为基本一致,它会使进程阻塞直到返回结果:
    • imap:与map用法一致,返回结果为选代器:
    • imap_unordered:与imap一致,但其并不保证返回结果与迭代传入的顺序一致:
    • map_async:异步map,与map用法一败,但是它是非阻塞的。其func函数可在poo1中一次被多个进程调用:
    • starmap:同map方法,但迭代中的元素也是可迭代的,并接受参数元组,然后进行元组拆包并将它们传送给给定的函数:
    • starmap_async;异步starmap,与starmap用法一致
    • close:关闭pool,使其不在接受新的任务;
    • terminate:结束工作进程。不再处理未处理的任务。
    • join:主进程阻塞等待子进程的退出,要在close或terminate之后使用:

pool.apply_async实现并行

from multiprocessing import Pool
 
def worker(arg):
  #主要的代码写这里
  
 
def main():
	#创建进程池
    ps=Pool(5)
    for i in range(10):
        #res=ps.apply(worker,args=(i,))          # 同步执行
        res=ps.apply_async(worker,args=(i,))  # 异步执行
 
    # 关闭进程池,停止接受其它进程
    ps.close()
    # 阻塞进程
    ps.join()
    print("主进程终止")
    res.get()#得到结果
 
if __name__ == '__main__':
    main()

注意:
1.这里的if name == ‘main’:是必须的.
2.如果worker函数需要多个参数,可以通过tuple作为形参

原文链接:https://blog.csdn.net/topleeyap/article/details/78981848

pool.map_async实现并行

from multiprocessing import Pool
 
def worker(arg):
  #主要的代码写这里
  
 
def main():
	#创建进程池
    ps=Pool(5)
    res=ps.map(worker,args=range(10)) #利用多进程的map函数
 
    # 关闭进程池,停止接受其它进程
    ps.close()
    # 阻塞进程
    ps.join()
    print("主进程终止")
    res.get() #得到结果
 
if __name__ == '__main__':
    main()

注意事项

注意:

  • 上述代码在Unix/Linux或mac OS系统中运行是没有问题的。但是,在Windows系统中使用多进程模块,就必须把有关进行的代码写在if __ name == '__ main __'语句体中。

  • 在Windows系统中,当使用多进程模块创建子进程时,会将整个主程序的代码重新执行一遍。如果不将相关的代码放在if name == 'main’语句体中,子进程在创建时会再次调用主程序,导致无限递归调用,最终导致程序崩溃。

  • 由于每个python模块 (python文件)都包含内置的变量__name__,当运行模块被执行的时候,__name__等于当前文件名 (包含了后缀.py)。如果import到其他模块中,则 __ name __ 等于模块名称 (不包含后缀py)

  • 而"__ main __ " 等于当前执行文件的名称 (包含了后缀.py)。所以当模块被直接执行时,__ name __ == ‘__ main __’ 结果为真;而当模块被import到其他模块中时, __ name __ == ‘main’ 结果为假,就不执行下面的代码。

  • 简而言之:__ name__ 是当前模块名,当模块被直接运行时模块名为__main__。 当模块被直接运行时,代码将被运行,当模块是被导入时代码不被运行。

pool.apply_async和pool.map_async的区别

pool.apply_asyncpool.map_asyncmultiprocessing.Pool类中的两种不同的并行处理方法。

  • pool.apply_async方法允许您将一个函数应用于一个参数,并在后台异步执行。它返回一个multiprocessing.pool.AsyncResult对象,您可以使用get方法获取函数的返回值。

  • pool.map_async方法允许您将一个函数应用于一个可迭代的参数,并在后台异步执行。它返回一个multiprocessing.pool.MapResult对象,您可以使用get方法获取函数的返回值列表。

主要区别如下:

  • 参数传递方式:pool.apply_async方法需要将参数作为元组传递给函数,而pool.map_async方法可以直接将参数作为可迭代对象传递给函数。

  • 返回值类型:pool.apply_async方法返回一个AsyncResult对象,而pool.map_async方法返回一个MapResult对象。

  • 获取结果方式:对于pool.apply_async,您需要使用get方法获取函数的返回值。而对于pool.map_async,您可以直接使用get方法获取函数的返回值列表。

总的来说,pool.apply_async适用于需要对单个参数进行并行处理的情况,而pool.map_async适用于需要对一个可迭代参数进行并行处理的情况。

ool的更多用法可以参考:
https://blog.csdn.net/fcku_88/article/details/99685540

以及pool中常见问题:
https://blog.csdn.net/qq_41131535/article/details/89706178

利用process派生类并行计算的框架

from multiprocessing import Process,Queue
#特别要注意,这里的Queue对象是multiprocessing中的!!!!!!!!!!

class worker(Process):
    def __init__(self, mission_queue):
        Process.__init__(self)
        self.mission_queue = mission_queue

    def run(self):
        while self.mission_queue.empty() == False:
           main_fun(self.mission_queue.get())

if __name__=='__main__':
	mission_queue = Queue()
	mission_queue.put(work)
	Workers=[]
	#创建n个进程类,并开启
	for i in range(n):
	    workrobot = worker(mission_queue)
	    workrobot.start()
	    Workers.append(workrobot)
	
	for worker in Workers:
	    worker.join()

join()和close()的区别

这里的pool.close()是说关闭pool,使其不再接受新的(主进程)任务

这里的pool.join()是说:主进程阻塞后,让子进程继续运行完成,子进程运行完后,再把主进程全部关掉。

要先close再join。

官方参考文档:https://docs.python.org/3/library/multiprocessing.html

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python中的多线程多进程都是用来实现并发执行的方式,可以同时执行多个任务,提高程序的运行效率。但是它们的实现方式和适用场景有所不同。 多线程是指在一个进程内创建多个线程,每个线程都可以独立执行任务。多线程适用于IO密集型的任务,如网络请求、文件读写等,因为在IO操作时可以让其他线程继续执行,提高了效率。Python中的`threading`模块可以用来创建和管理线程。 多进程是指在操作系统中同时创建多个进程,每个进程都有自己的地址空间和系统资源,可以独立执行任务。多进程适用于CPU密集型的任务,如大量计算、图像处理等,因为每个进程都可以利用多核CPU进行并行计算,提高了效率。Python中的`multiprocessing`模块可以用来创建和管理进程。 需要注意的是,Python中的多线程由于全局解释器锁(GIL)的存在,不能实现真正的并行运行。如果需要充分利用多核CPU进行并行计算,应该使用多进程。但是在某些特定情况下,多线程仍然可以提高程序的效率,如IO密集型任务。 总结一下: - 多线程适用于IO密集型任务,可以提高程序的效率。 - 多进程适用于CPU密集型任务,可以充分利用多核CPU进行并行计算。 - Python中的多线程由于GIL的存在,不能实现真正的并行运行。如果需要充分利用多核CPU,应该使用多进程

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值