Python多线程与线程池(python线程池ThreadPoolExecutor)concurrent.futures高级别异步执行封装

Python多线程与线程池

一、Python多线程

在进行复杂的计算或处理大量数据时,可以通过创建多个线程来同时执行多个任务,从而提高程序的执行效率。这种技术称为多线程编程。

1.1 线程简介

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
在这里插入图片描述

1.2 Python中的多线程

Python中的threading模块提供了对线程的支持。使用threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法。

import threading

class MyThread(threading.Thread):
    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n
    
    def run(self):
        print('running task', self.n)

t1 = MyThread(1)
t2 = MyThread(2)

t1.start()
t2.start()

在这里插入图片描述

1.3 GIL限制

由于Python解释器设计中的全局解释器锁(Global Interpreter Lock,GIL)的存在,使得Python的多线程并不能利用多核优势。GIL是计算机程序设计语言解释器用于同步线程的工具,使得任何时刻只有一个线程在执行,即使在多核CPU平台上,Python的线程也无法同时执行。

在这里插入图片描述

二、线程池

线程池是一种基于池化思想管理线程的工具。在开始任务时不再重新创建新的线程,而是直接从线程池中获取一个空闲线程来执行。如果线程池中没有空闲线程,新的任务就会等待(排队),直到有线程空闲。当任务执行完毕后,线程并不立即销毁,而是返回线程池等待下次被利用。

2.1 Python中的线程池

Python的concurrent.futures模块提供了高级别的异步执行封装,包括线程池ThreadPoolExecutor和进程池ProcessPoolExecutor,它们都是Executor的子类。

from concurrent.futures import ThreadPoolExecutor

def func(n):
    print(n)

with ThreadPoolExecutor(max_workers=4) as executor:
    executor.map(func, range(1,5))

其中max_workers参数表示线程池中最多可以同时运行的线程数量。

在这里插入图片描述

线程池优势

Python线程池具有以下优势:

  1. 控制并发数:Python的线程池可以控制系统中运行的线程数量,避免了因为创建过多线程而导致系统资源耗尽。

  2. 提高性能:通过复用已经创建的线程,可以避免频繁地创建和销毁线程所带来的性能开销。

  3. 简化代码:使用线程池,我们只需要将任务提交给线程池,无需手动管理每个线程的生命周期。

  4. 异步处理:Python的线程池提供了异步处理任务的能力。当我们提交任务给线程池后,线程池会在后台进行处理,不会阻塞主程序的执行。

  5. 调度方便:线程池还提供了一些调度功能,如定时执行、周期执行等。

  6. 任务队列:线程池内部维护了一个任务队列,如果线程池中的所有线程都在忙,新来的任务会被放入队列中等待执行,这样可以保证所有提交给线程池的任务都会被执行,不会丢失。

总的来说,使用Python线程池可以简化并发编程的复杂性,提高程序的性能和稳定性。

三、代码分析

import requests
from requests.models import PreparedRequest
import json
import concurrent.futures

def get_score_models(url):
    url_score = "https://bizapi.csdn.net/trends/api/v1/get-article-score"

    headers = {
        "accept": "application/json, text/plain, */*",
        "x-ca-key": "203930474",
        "x-ca-nonce": "22cd11a0-760a-45c1-8089-14e53123a852",
        "x-ca-signature": "RaEczPkQ22Ep/k9/AI737gCtn8qX67CV/uGdhQiPIdQ=",
        "x-ca-signature-headers": "x-ca-key,x-ca-nonce",
        "x-ca-signed-content-type": "multipart/form-data"
    }

    data = {"url": url}
    response = send_request(url_score, data, headers)
    data1 = response.json()

    score_model = data1["data"]

    return score_model


def send_request(url, data, headers):
    session = requests.Session()
    prepared_request = PreparedRequest()
    prepared_request.prepare(method='POST', url=url,
                             headers=headers, data=data)
    return session.send(prepared_request)


def process_article_json(article):
    # score_model = get_score_models(article['article_url'])
    score_model = get_score_models(article['url'])
    article['article_score'] = score_model['score']
    print(article["url"])
    return article


if __name__ == '__main__':
    # 读取articles.json文件
    with open('articles.json', 'r') as f:
        articles = json.load(f)

    # 创建一个 ThreadPoolExecutor 实例,max_workers 表示线程池中最多可以同时运行的线程数量
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        # 使用 map 函数将 process_article_json 应用到每个元素,并在多线程环境下并行处理
        processed_articles = list(executor.map(process_article_json, articles))

    # 保存处理后的结果到新的JSON文件
    output_file = 'processed_articles.json'
    with open(output_file, 'w') as f:
        json.dump(processed_articles, f, ensure_ascii=False, indent=4)

import requests
from requests.models import PreparedRequest
import json
import concurrent.futures
def get_score_models(url):
url_score = “https://bizapi.csdn.net/trends/api/v1/get-article-score”
headers = {
“accept”: “application/json, text/plain, /”,
“x-ca-key”: “203930474”,
“x-ca-nonce”: “22cd11a0-760a-45c1-8089-14e53123a852”,
“x-ca-signature”: “RaEczPkQ22Ep/k9/AI737gCtn8qX67CV/uGdhQiPIdQ=”,
“x-ca-signature-headers”: “x-ca-key,x-ca-nonce”,
“x-ca-signed-content-type”: “multipart/form-data”
}
data = {“url”: url}
response = send_request(url_score, data, headers)
data1 = response.json()
score_model = data1[“data”]
return score_model
def send_request(url, data, headers):
session = requests.Session()
prepared_request = PreparedRequest()
prepared_request.prepare(method=‘POST’, url=url,
headers=headers, data=data)
return session.send(prepared_request)
def process_article_json(article):
# score_model = get_score_models(article[‘article_url’])
score_model = get_score_models(article[‘url’])
article[‘article_score’] = score_model[‘score’]
print(article[“url”])
return article
if name == ‘main’:
# 读取articles.json文件
with open(‘articles.json’, ‘r’) as f:
articles = json.load(f)
# 创建一个 ThreadPoolExecutor 实例,max_workers 表示线程池中最多可以同时运行的线程数量
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# 使用 map 函数将 process_article_json 应用到每个元素,并在多线程环境下并行处理
processed_articles = list(executor.map(process_article_json, articles))
# 保存处理后的结果到新的JSON文件
output_file = ‘processed_articles.json’
with open(output_file, ‘w’) as f:
json.dump(processed_articles, f, ensure_ascii=False, indent=4)

在这里插入图片描述

以上给出的代码片段主要涉及到的是线程池的使用。具体来说,首先从一个名为articles.json的文件中读取文章信息,然后利用线程池并发地获取每篇文章的评分,并将评分添加到文章信息中,最后将处理后的文章信息保存到新的JSON文件。

代码的主要部分如下:

with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    processed_articles = list(executor.map(process_article_json, articles))

在这里插入图片描述

这里,首先创建了一个ThreadPoolExecutor实例,并设置最大并发线程数为5。然后使用executor.map()函数将process_article_json函数应用到articles列表的每个元素上,这样就可以在多线程环境下并行处理每篇文章了。由于executor.map()函数返回的是一个迭代器,因此需要用list()函数将其转换为列表。

这种方式可以有效地提高处理大量文章信息的效率,特别是当获取文章评分的过程涉及到网络请求等I/O操作时,通过线程池并发处理可以显著减少总的处理时间。

在这里插入图片描述

四、参考资料

20230817

 with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
            future_to_ip = {executor.submit(check_ping, ip): ip for ip in ips}
            for future in concurrent.futures.as_completed(future_to_ip):
                ip = future_to_ip[future]
                try:
                    if future.result():
                        print(f"IP [{ip}] 可以ping通")
                        return True
                except Exception as exc:
                    print('%r generated an exception: %s' % (ip, exc))

这段代码是使用Python的concurrent.futures模块创建一个线程池来并发执行任务。每个任务都在一个独立的线程中运行,最大并发线程数由max_workers参数指定。

  1. with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:

    这一行创建了一个线程池执行器(ThreadPoolExecutor),最大工作线程数量为20。

  2. future_to_ip = {executor.submit(check_ping, ip): ip for ip in ips}

    这一行通过线程池执行器提交任务。每个任务都是对函数check_ping的调用,并传入一个IP地址。这会返回一个Future对象,表示待完成的操作。然后将每个Future对象与对应的IP地址映射到字典future_to_ip中。

  3. for future in concurrent.futures.as_completed(future_to_ip):

    这一行遍历所有已完成的Future对象。as_completed函数返回一个迭代器,当一个Future对象完成时,它就会产生一个新的值。

    for future in concurrent.futures.as_completed(future_to_ip)这段代码是阻塞等待的。这个语句会迭代返回已经完成的Future实例。如果没有完成的Future实例,它就会等待,直到有Future实例完成。

    concurrent.futures.as_completed函数返回一个迭代器,这个迭代器会在每次Future实例完成(无论成功还是失败)时产生该Future。所以,在这个循环中,你可以立即处理已完成的任务,而不必等待所有任务都完成。但是,如果所有的Future都还未完成,那么这个循环就会阻塞,直到至少有一个Future完成。

  4. ip = future_to_ip[future]

    这一行从字典中获取与当前Future对象对应的IP地址。

  5. if future.result():

    这一行获取Future对象的结果。如果check_ping函数返回True,那么就打印出可以ping通的IP地址,并返回True。

  6. 如果在获取Future对象的结果时发生异常,那么就捕获这个异常,并打印出引起异常的IP地址和异常信息。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Python多线程可以使用内置的`threading`模块来实现。这个模块提供了一些对象和方法,可以方便地创建和管理线程。 以下是一个简单的多线程示例,它创建了两个线程,每个线程都打印数字1到5: ```python import threading def print_numbers(): for i in range(1, 6): print(threading.current_thread().name, i) # 创建两个线程 t1 = threading.Thread(target=print_numbers) t2 = threading.Thread(target=print_numbers) # 启动线程 t1.start() t2.start() # 等待线程结束 t1.join() t2.join() print("All threads have finished.") ``` 在这个示例中,我们首先定义了一个`print_numbers()`函数,它用于打印数字1到5,并且在每个数字前面打印线程的名称。然后,我们创建了两个线程`t1`和`t2`,它们都指向`print_numbers()`函数。接下来,我们启动这两个线程,并等待它们完成,最后输出"All threads have finished."。 注意,`threading.current_thread().name`用于获取当前线程的名称。在这个示例中,我们没有指定线程的名称,因此它们将默认为"Thread-1"和"Thread-2"。 多线程可以提高程序的执行效率,但是也需要注意线程安全问题,比如并发访问共享变量可能会导致数据不一致的问题。因此,在编写多线程程序时,需要特别注意线程安全问题。 ### 回答2: Python多线程是指在一个程序中同时执行多个线程,每个线程都可以独立执行不同的任务。Python多线程是基于线程模块实现的,通过创建多个线程对象来实现多线程的功能。 Python多线程的使用需要导入threading模块,使用threading.Thread类来创建线程对象。通过调用线程对象的start()方法,线程就会开始执行。线程可以是执行同一个函数,也可以是执行不同的函数,甚至可以是执行不同的类的方法。 线程之间可以共享全局变量,但需要避免多个线程同时修改全局变量的情况,可以通过互斥锁机制来保证数据的一致性。 Python多线程的优点是能够提高程序的执行效率,特别是在IO操作较多的情况下,多线程可以充分利用CPU的空闲时间。另外,多线程还可以实现一些并发的功能,例如同时下载多个文件、同时处理多个网络请求等。 然而,Python多线程在处理CPU密集型任务上并不适用,因为在Python中,多线程并不能利用多核CPU的优势,由于Python的GIL(全局解释器锁)机制,多线程在CPU密集型任务上的效率并不比单线程高。 总结来说,Python多线程适用于IO密集型任务,能够提高程序的执行效率和实现并发的功能,但对于CPU密集型任务,单线程可能更适合。 ### 回答3: Python 多线程是指在一个程序中同时运行多个线程,每个线程独立执行其任务。Python 中的多线程可以通过使用 threading 模块来实现。 在 Python 中,多线程的主要优势是能够提升程序的执行效率。通过多线程,可以将耗时较长的任务分配给不同的线程来并行执行,从而缩短程序的总执行时间。这尤其适用于那些需要频繁进行网络请求、IO 操作或者计算密集型任务的程序。 使用 Python 的 threading 模块可以很方便地创建和管理线程。通过创建 Thread 对象并传入要执行的函数,就可以创建一个新的线程。可以使用 start() 方法来启动线程,并使用 join() 方法来等待线程执行完成。 需要注意的是,Python 中的多线程并不能真正实现并行执行,而是通过在不同任务之间快速切换来模拟并行。这是由于 Python 的全局解释器锁(GIL)的存在,它使得同一时间只有一个线程能够执行 Python 的字节码。因此,在计算密集型任务上,使用多线程并不能获得真正的并行加速。 另外,多线程在处理共享资源时需要注意线程安全问题。多个线程同时访问和修改共享数据可能会导致数据不一致或者竞争条件。在这种情况下,可以通过使用锁(Lock)等同步机制来确保数据的正确访问和更新。 总而言之,Python 多线程可以提升程序的执行效率,适用于需要进行网络请求、IO 操作或者并发处理的任务。然而,在计算密集型任务上,并不能实现真正的并行加速。同时,在处理共享资源时需要注意线程安全问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dontla

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值