首页 > 系统相关 >进程池和线程池

进程池和线程池

时间:2023-07-07 21:00:46浏览次数:36  
标签:__ res 任务 线程 进程 pool

一、进程池

1、进程池 ProcessPoolExecutor

优点:

  1. 减少进程创建和销毁的开销: 创建和销毁进程是一个相对耗时的操作,涉及到操作系统的系统调用和资源分配。使用进程池,可以预先创建一组进程,并在需要时重用这些进程,避免了频繁的进程创建和销毁开销,提高了程序的性能和效率。

  2. 控制并发进程数量: 进程池允许你限制并发执行的进程数量,避免进程数量过多导致系统资源耗尽。通过设置进程池的大小,可以根据系统的处理能力和任务的特性来合理分配进程资源,确保进程的数量在适当范围内。

  3. 充分利用多核 CPU: 进程池可以充分利用多核 CPU 的优势,通过并行执行多个进程,提高了系统的处理能力和任务的执行效率。每个进程都拥有自己的 Python 解释器和 GIL(对于 CPython 解释器),实现了真正的并行执行,适用于 CPU 密集型任务。

  4. 避免进程资源竞争和冲突: 在多进程编程中,如果进程数量过多,可能会导致进程之间的资源竞争和冲突,如共享内存的同步问题等。使用进程池可以限制进程数量,避免过多的进程之间产生资源竞争和冲突,提高了程序的稳定性和可靠性。

  5. 提供任务队列和排队机制: 进程池通常与任务队列结合使用,可以将任务放入队列中,进程池会自动从队列中取出任务进行处理。这样可以避免任务丢失和重复执行的问题,并提供了一种简单而高效的任务调度和排队机制,使任务的处理更加有序和可控。

总的来说,进程池通过管理和重用进程、限制并发数量、提高任务调度效率等方式,优化了多进程编程的性能和资源利用,提供了一种可靠且高效的进程管理机制。在需要处理大量并发任务、充分利用多核 CPU 或涉及到共享资源的场景下,使用进程池可以有效地提升系统的性能和可扩展性。

2、使用进程池执行task任务,调用回调函数

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

def task(n, m):
    return n + m  # 1+2

def task1():
    # ...
    return {
        'username': 'kevin',
        'password': 123
    }

def callback(res):
    print(res.result())  # 固定写法

def callback1(res):
    print(res.result())
    print(res.result()['username'])

if __name__ == '__main__':
    """开一个进程池,提前放进去多个进程"""
    # max_workers 参数设置存放的进程数
    pool = ProcessPoolExecutor(2)  # 池子里面有2个工作者,其实就是实例化了5个进程

    # 有了进程池,我们现在往池子里面丢任务
    # pool.submit(task, 1, 2)
    # 进程池要主动调一个回调函数,来把结果给到我们,回调函数需要我们自己提前写好
    pool.submit(task, n=1, m=2).add_done_callback(callback)
    pool.submit(task1).add_done_callback(callback1)
    pool.shutdown()  # join + close
    print(123)

二、线程池

1、线程池是一种用于管理和重用线程的技术,优点如下:

  1. 减少线程创建和销毁的开销: 创建和销毁线程是一个相对昂贵的操作,涉及到操作系统的系统调用和资源分配。使用线程池,可以预先创建一组线程,并在需要时重用这些线程,避免了频繁的线程创建和销毁开销,提高了程序的性能和效率。

  2. 控制并发线程数量: 线程池允许你限制并发执行的线程数量,避免线程数量过多导致系统资源耗尽。通过设置线程池的大小,可以根据系统的处理能力和任务的特性来合理分配线程资源,确保线程的数量在适当范围内。

  3. 提高响应性和吞吐量: 线程池可以有效地管理和调度线程,通过并发执行多个任务,提高了系统的响应性和任务的处理吞吐量。当一个任务完成后,线程池可以立即将下一个任务分配给空闲的线程,减少了任务等待时间,提高了任务的执行效率。

  4. 避免线程资源竞争和冲突: 在多线程编程中,如果线程数量过多,可能会导致线程之间的资源竞争和冲突,如锁竞争、死锁等问题。使用线程池可以限制线程数量,避免过多的线程之间产生资源竞争和冲突,提高了程序的稳定性和可靠性。

  5. 提供任务队列和排队机制: 线程池通常与任务队列结合使用,可以将任务放入队列中,线程池会自动从队列中取出任务进行处理。这样可以避免任务丢失和重复执行的问题,并提供了一种简单而高效的任务调度和排队机制,使任务的处理更加有序和可控。

总的来说,线程池通过管理和重用线程、限制并发数量、提高任务调度效率等方式,优化了多线程编程的性能和资源利用,提供了一种可靠且高效的线程管理机制。在需要处理大量并发任务的场景下,使用线程池可以有效地提升系统的性能和可扩展性。

2、线程池 ThreadPoolExecutor

from concurrent.futures  import  ThreadPoolExecutor

def task(n, m):
    return n + m  # 1+2

def task1():
    # ...
    return {
        'username': 'kevin',
        'password': 123
    }

def callback(res):
    print(res.result())  # 固定写法

def callback1(res):
    print(res.result())
    print(res.result()['username'])

if __name__ == '__main__':
    """开一个进程池,提前放进去多个进程"""
    # max_workers 参数设置存放的进程数
    pool = ThreadPoolExecutor(2)  # 池子里面有2个工作者,其实就是实例化了2个线程

    # 有了进程池,我们现在往池子里面丢任务
    # pool.submit(task, 1, 2)
    # 进程池要主动调一个回调函数,来把结果给到我们,回调函数需要我们自己提前写好
    pool.submit(task, n=1, m=2).add_done_callback(callback)
    pool.submit(task1).add_done_callback(callback1)
    pool.shutdown()  # join + close
    print(123)

3、使用线程池爬取网页

这里爬取到的网页内容是二进制的,所以写入用wb模式

import requests
from  concurrent.futures  import  ThreadPoolExecutor, ProcessPoolExecutor

def get_page(url):
    res = requests.get(url)  # 爬取网页
    name = url.rsplit('/')[-1] + '.html'
    return  {'name': name, 'text': res.content}

# 使用回调函数时会返回一个 concurrent.futures.Future 对象,
# 它代表了异步操作的结果。res.result() 是 Future 对象的方法,用于获取异步操作的结果.
def call_back(res):
    print(res.result()['name'])   # 回调函数获取的是task的返回值,这里get_page返回值是一个字典
    with open(res.result()['name'], 'wb') as f:
        f.write(res.result()['text'])

if __name__ == '__main__':
    pool = ThreadPoolExecutor(2)
    urls = ['http://www.baidu.com', 'http://www.cnblogs.com', 'http://www.taobao.com']
    for url in urls:
        pool.submit(get_page, url).add_done_callback(call_back)

# www.baidu.com.html
# www.cnblogs.com.html
# www.taobao.com.html

三、进程池的两种实现机制

multiprocessing.Pool 和 concurrent.futures.ProcessPoolExecutor

1、multiprocessing.Pool的优点和适用场景:

  • 优点:
    • 使用Python标准库中的multiprocessing模块,易于使用和理解。
    • 提供了方便的方法,如apply_async用于异步提交任务,closejoin用于管理进程池。
    • 可以根据并发需求灵活设置进程池的大小。
  • 缺点:
    • 在某些情况下,由于进程间通信的开销和资源消耗,使用多进程可能会导致更高的系统开销。
    • 在Windows系统中,multiprocessing.Pool使用的是fork方式,而不是spawn方式,可能会受到一些限制。
  • 适用场景:
    • 一般的并发连接处理。
    • 对于计算密集型任务,其中存在大量CPU计算,并且不涉及过多的进程间通信。

2、concurrent.futures.ProcessPoolExecutor的优点和适用场景:

  • 优点:
    • 基于concurrent.futures模块,提供了高级的异步执行和进程池的接口。
    • 可以更方便地使用上下文管理器(with语句)来管理进程池的生命周期。
    • 具有与ThreadPoolExecutor相同的接口,使得在需要切换到线程池的情况下更加方便。
  • 缺点:
    • 在某些情况下,由于进程间通信的开销和资源消耗,使用多进程可能会导致更高的系统开销。
    • 在Windows系统中,仍然受到一些限制,因为它仍然使用的是multiprocessing.Pool
  • 适用场景:
    • 对于需要更高级和灵活接口的进程池需求。
    • 可以与concurrent.futures.ThreadPoolExecutor一起使用,在不同场景之间进行切换。

四、结合socket编程实现并发接收客户端

服务端方式一: ProcessPoolExecutor

import socket
from concurrent.futures import ProcessPoolExecutor

def handle_client(sock):
    while True:
        try:
            data = sock.recv(1024)
            print('客户端发来的消息是:%s' % data)
            if len(data) == 0:
                break
            sock.send(b'this is server!')
        except ConnectionResetError as e:
            print(e)
            break

    sock.close()

if __name__ == '__main__':
    server = socket.socket()
    server.bind(('127.0.0.1', 8001))
    server.listen(4)

    with ProcessPoolExecutor(5) as executor:  # 创建进程池,最大进程数为5
        while True:
            sock, addr = server.accept()
            executor.submit(handle_client, sock)  # 使用进程池提交任务

    server.close()

服务端方式二: 

import socket
import multiprocessing as mp

def handle_client(sock):
    while True:
        try:
            data = sock.recv(1024)
            print('客户端发来的消息是:%s' % data)
            if len(data) == 0:
                break
            sock.send(b'this is server!')
        except ConnectionResetError as e:
            print(e)
            break

    sock.close()

if __name__ == '__main__':
    server = socket.socket()
    server.bind(('127.0.0.1', 8001))
    server.listen(4)

    pool = mp.Pool(processes=5)  # 创建进程池,指定进程数为5

    while True:
        sock, addr = server.accept()
        pool.apply_async(handle_client, args=(sock,))  # 使用进程池异步提交任务

    pool.close()
    pool.join()
    server.close()

客户端

import socket

# 1. 实例化对象
client = socket.socket()  # 默认的参数就是基于网络的tcp socket

# 2. 连接服务端
client.connect(('127.0.0.1', 8001))

while True:
    res = input('请输入你要发送的消息:')

    if res == 'exit':
        break

    if res == '':
        continue

    # 3. 给服务端发送消息
    client.send(res.encode('utf8'))

    # 4. 接受服务端发来的消息
    data = client.recv(1024)
    print('服务端发来的消息:%s' % data)

# 5. 断开连接
client.close()

  

 

标签:__,res,任务,线程,进程,pool
From: https://www.cnblogs.com/dgp-zjz/p/17536048.html

相关文章

  • 进程与线程的一个简单解释
    进程(process)和线程(thread)是操作系统的基本概念,但是它们比较抽象,不容易掌握。最近,我读到一篇材料,发现有一个很好的类比,可以把它们解释地清晰易懂。1. 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。2. 假定工厂的电力有限,一次只能供给一个车间......
  • Java线程池详解:Future的使用和实现
    提交到线程池中执行的异步任务都会返回一个任务的Future,所以这里先介绍一下Future的使用和实现。异步任务通常会被提交到线程池中去执行,但任务并非提交到线程池后就不管不顾了,在某些时刻我们希望能够取消任务,同时也希望在任务执行完成后获取到任务的执行结果。Java提供了Futur......
  • 多线程知识:三个线程如何交替打印ABC循环100次
    本文博主给大家讲解一道网上非常经典的多线程面试题目。关于三个线程如何交替打印ABC循环100次的问题。下文实现代码都基于Java代码在单个JVM内实现。问题描述给定三个线程,分别命名为A、B、C,要求这三个线程按照顺序交替打印ABC,每个字母打印100次,最终输出结果为:ABCABC.......
  • Java中的多线程
    1.线程实现1.1线程创建(三种方法)Java下载图片的方法:通过FileUtils.copyURLToFile方法获取线程名字Thread.currentThread.getName()classWebDownloader{//下载方法publicvoiddownloader(Stringurl,Stringname){try{FileUtils.co......
  • 线程六态
    在Java中,Thread类定义了六个状态来表示线程的生命周期。这些状态通过Thread类中的常量来表示,分别是: 1.NEW(新建状态):当一个Thread对象被创建时,它处于新建状态。这时候线程还没有开始执行,直到调用了start()方法。 2.RUNNABLE(可运行状态):当一个线程正在Java虚拟机中......
  • 父子进程
    #include<stdlib.h>#include<stdio.h>#include<unistd.h>//父子进程遵循读时共享,写时复制的原则intvar=100;intmain(){pid_tpid;pid=fork();if(pid==-1){perror("forkerror");exit(1);}elseif(pid>0){......
  • 循环产生若干个子进程
    #include<stdlib.h>#include<stdio.h>#include<unistd.h>intmain(){intn;scanf("%d",&n);//循环创建n个进程inti;for(i=0;i<n;i++){if(fork()==0){break;}}if(i==n){......
  • 巧用 awk 批量杀进程
    今天遇到线上的一个问题:我需要批量杀死某台机器的PHP进程,该怎么办?注意,不是php-fpm,是常驻任务。如果是一个进程,那就好办了,ps-ef|grepphp,找到PID然后kill-9PID……那批量怎么搞呢?答案是awk命令。AWK是一种处理文本文件的语言,是一个强大的文本分析工具。有了......
  • python基础day39 生产者消费者模型和线程相关
    如何查看进程的id号进程都有几个属性:进程名、进程id号(pid--->processid)每个进程都有一个唯一的id号,通过这个id号就能找到这个进程importosimporttimedeftask():print("task中的子进程号:",os.getpid())print("主进程中的进程号:",os.getppid())#parent......
  • 多线程python
    如何开启进程使用的是内置的模块:multiprocessfrommultiprocessingimportProcessdeftask():withopen('a.txt','w',encoding="utf8")asf:f.write('helloworld')#开一个进程来执行task这个任务#如何开进程"""在Wind......