iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python多线程中线程数量如何控制
  • 616
分享到

Python多线程中线程数量如何控制

Python线程数量控制Python线程控制 2023-01-06 12:01:30 616人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

前言 前段时间学习了python的多线程爬虫,当时爬取一个图片网站,开启多线程后,并没有限制线程的数量,也就是说,如果下载1000张图片,会一次性开启1000个子线程同时进行下载 现

前言

前段时间学习python多线程爬虫,当时爬取一个图片网站,开启多线程后,并没有限制线程的数量,也就是说,如果下载1000张图片,会一次性开启1000个子线程同时进行下载

现在希望控制线程数量:例如每次只下载5张,当下载完成后再下载另外5张,直至全部完成

查了一些资料,发现在Python中,threading 模块有提供 Semaphore类 和 BoundedSemaphore类来限制线程数

官网给出例子如下:

信号量通常用于保护容量有限的资源,例如数据库服务器。在资源大小固定的任何情况下,都应使用有界信号量。在产生任何工作线程之前,您的主线程将初始化信号量:

maxconnections = 5
# ...
pool_sema = BoundedSemaphore(value=maxconnections)

产生后,工作线程在需要连接到服务器时会调用信号量的获取和释放方法:

改造之前的多线程爬虫

首先贴出原来的代码

# -*- coding:utf-8 -*-
import requests
from requests.exceptions import RequestException
import os, time
import re
from lxml import etree
import threading

lock = threading.Lock()
def get_html(url):
    """
    定义一个方法,用于获取一个url页面的响应内容
    :param url: 要访问的url
    :return: 响应内容
    """
    response = requests.get(url, timeout=10)
    # print(response.status_code)
    try:
        if response.status_code == 200:

            # print(response.text)
            return response.text
        else:
             return None
    except RequestException:
        print("请求失败")
        # return None


def parse_html(html_text):
    """
    定义一个方法,用于解析页面内容,提取图片url
    :param html_text:
    :return:一个页面的图片url集合
    """
    html = etree.HTML(html_text)

    if len(html) > 0:
        img_src = html.xpath("//img[@class='photothumb lazy']/@data-original")  # 元素提取方法
        # print(img_src)
        return img_src

    else:
        print("解析页面元素失败")

def get_image_pages(url):
    """
    获取所查询图片结果的所有页码
    :param url: 查询图片url
    :return: 总页码数
    """

    html_text = get_html(url)  # 获取搜索url响应内容
    # print(html_text)
    if html_text is not None:
        html = etree.HTML(html_text)  # 生成XPath解析对象
        last_page = html.xpath("//div[@class='pages']//a[last()]/@href")  # 提取最后一页所在href链接
        print(last_page)
        if last_page:
            max_page = re.compile(r'(\d+)', re.S).search(last_page[0]).group()  # 使用正则表达式提取链接中的页码数字
            print(max_page)
            print(type(max_page))
            return int(max_page)  # 将字符串页码转为整数并返回
        else:
            print("暂无数据")
            return None
    else:
        print("查询结果失败")


def get_all_image_url(page_number):
    """
    获取所有图片的下载url
    :param page_number: 爬取页码
    :return: 所有图片url的集合
    """

    base_url = 'https://imgbin.com/free-png/naruto/'
    image_urls = []

    x = 1  # 定义一个标识,用于给每个图片url编号,从1递增
    for i in range(1, page_number):
        url = base_url + str(i)  # 根据页码遍历请求url
        try:
            html = get_html(url)  # 解析每个页面的内容
            if html:
                data = parse_html(html)  # 提取页面中的图片url
                # print(data)
                # time.sleep(3)
                if data:
                    for j in data:
                        image_urls.append({
                            'name': x,
                            'value': j
                        })
                        x += 1  # 每提取一个图片url,标识x增加1
        except RequestException as f:
            print("遇到错误:", f)
            continue
    # print(image_urls)
    return image_urls

def get_image_content(url):
    """请求图片url,返回二进制内容"""
    # print("正在下载", url)
    try:
        r = requests.get(url, timeout=15)
        if r.status_code == 200:
            return r.content
        return None
    except RequestException:
        return None

def main(url, image_name):
    """
    主函数:实现下载图片功能
    :param url: 图片url
    :param image_name: 图片名称
    :return:
    """
    semaphore.acquire()  # 加,限制线程数
    print('当前子线程: {}'.fORMat(threading.current_thread().name))
    save_path = os.path.dirname(os.path.abspath('.')) + '/pics/'
    try:
        file_path = '{0}/{1}.jpg'.format(save_path, image_name)
        if not os.path.exists(file_path):  # 判断是否存在文件,不存在则爬取
            with open(file_path, 'wb') as f:
                f.write(get_image_content(url))
                f.close()

                print('第{}个文件保存成功'.format(image_name))

        else:
            print("第{}个文件已存在".format(image_name))

        semaphore.release()  # 解锁imgbin-多线程-重写run方法.py

    except FileNotFoundError as f:
        print("第{}个文件下载时遇到错误,url为:{}:".format(image_name, url))
        print("报错:", f)
        raise

    except TypeError as e:
        print("第{}个文件下载时遇到错误,url为:{}:".format(image_name, url))
        print("报错:", e)

class MyThread(threading.Thread):
    """继承Thread类重写run方法创建新进程"""
    def __init__(self, func, args):
        """

        :param func: run方法中要调用的函数名
        :param args: func函数所需的参数
        """
        threading.Thread.__init__(self)
        self.func = func
        self.args = args

    def run(self):
        print('当前子线程: {}'.format(threading.current_thread().name))
        self.func(self.args[0], self.args[1])
        # 调用func函数
        # 因为这里的func函数其实是上述的main()函数,它需要2个参数;args传入的是个参数元组,拆解开来传入


if __name__ == '__main__':
    start = time.time()
    print('这是主线程:{}'.format(threading.current_thread().name))

    urls = get_all_image_url(5)  # 获取所有图片url列表
    thread_list = []  # 定义一个列表,向里面追加线程
    semaphore = threading.BoundedSemaphore(5) # 或使用Semaphore方法
    for t in urls:
        # print(i)

        m = MyThread(main, (t["value"], t["name"]))  # 调用MyThread类,得到一个实例

        thread_list.append(m)

    for m in thread_list:

        m.start()  # 调用start()方法,开始执行

    for m in thread_list:
        m.join()  # 子线程调用join()方法,使主线程等待子线程运行完毕之后才退出


    end = time.time()
    print(end-start)
    # get_image_pages("Https://imgbin.com/free-png/Naruto")

将代码进行改造

1、下面的第8、9行表示调用 threading 的 BoundedSemaphore类,初始化信号量为5,把结果赋给变量 pool_sema

if __name__ == '__main__':
    start = time.time()
    print('这是主线程:{}'.format(threading.current_thread().name))

    urls = get_all_image_url(5)  # 获取所有图片url列表
    thread_list = []  # 定义一个列表,向里面追加线程
    # 更多Python相关视频、资料加群778463939免费获取
    max_connections = 5  # 定义最大线程数
    pool_sema = threading.BoundedSemaphore(max_connections) # 或使用Semaphore方法
    for t in urls:
        # print(i)

        m = MyThread(main, (t["value"], t["name"]))  # 调用MyThread类,得到一个实例

        thread_list.append(m)

    for m in thread_list:

        m.start()  # 调用start()方法,开始执行

    for m in thread_list:
        m.join()  # 子线程调用join()方法,使主线程等待子线程运行完毕之后才退出


    end = time.time()
    print(end-start)

2、修改main()函数

(1)方法一:通过with语句实现,第9行添加 with pool_sema

使用 with 语句来获得一个锁、条件变量或信号量,相当于调用 acquire();离开 with 块后,会自动调用 release()

def main(url, image_name):
    """
    主函数:实现下载图片功能
    :param url: 图片url
    :param image_name: 图片名称
    :return:
    """

    with pool_sema:
        print('当前子线程: {}'.format(threading.current_thread().name))
        save_path = os.path.dirname(os.path.abspath('.')) + '/pics/'
        try:
            file_path = '{0}/{1}.jpg'.format(save_path, image_name)
            if not os.path.exists(file_path):  # 判断是否存在文件,不存在则爬取
                with open(file_path, 'wb') as f:
                    f.write(get_image_content(url))
                    f.close()

                    print('第{}个文件保存成功'.format(image_name))

            else:
                print("第{}个文件已存在".format(image_name))

        except FileNotFoundError as f:
            print("第{}个文件下载时遇到错误,url为:{}:".format(image_name, url))
            print("报错:", f)
            raise

        except TypeError as e:
            print("第{}个文件下载时遇到错误,url为:{}:".format(image_name, url))
            print("报错:", e)

(2)方法二:直接使用 acquire()和 release()

下面的第8行调用 acquire(),第24行调用release()

def main(url, image_name):
    """
    主函数:实现下载图片功能
    :param url: 图片url
    :param image_name: 图片名称
    :return:
    """
    pool_sema.acquire()  # 加锁,限制线程数
    # with pool_sema:
    print('当前子线程: {}'.format(threading.current_thread().name))
    save_path = os.path.dirname(os.path.abspath('.')) + '/pics/'
    try:
        file_path = '{0}/{1}.jpg'.format(save_path, image_name)
        if not os.path.exists(file_path):  # 判断是否存在文件,不存在则爬取
            with open(file_path, 'wb') as f:
                f.write(get_image_content(url))
                f.close()

                print('第{}个文件保存成功'.format(image_name))

        else:
            print("第{}个文件已存在".format(image_name))

        pool_sema.release()  # 解锁imgbin-多线程-重写run方法.py

    except FileNotFoundError as f:
        print("第{}个文件下载时遇到错误,url为:{}:".format(image_name, url))
        print("报错:", f)
        raise

    except TypeError as e:
        print("第{}个文件下载时遇到错误,url为:{}:".format(image_name, url))
        print("报错:", e)

最终效果是一样的,每次启用5个线程,完成后再启动下一批

 到此这篇关于Python多线程中线程数量如何控制的文章就介绍到这了,更多相关Python 线程数量控制内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Python多线程中线程数量如何控制

本文链接: https://www.lsjlt.com/news/176916.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Python多线程中线程数量如何控制
    前言 前段时间学习了python的多线程爬虫,当时爬取一个图片网站,开启多线程后,并没有限制线程的数量,也就是说,如果下载1000张图片,会一次性开启1000个子线程同时进行下载 现...
    99+
    2023-01-06
    Python 线程数量控制 Python 线程控制
  • python自定义线程池控制线程数量
    1.自定义线程池 import threading import Queue import time queue = Queue.Queue() def put_data_in_queue(): for i in xrang...
    99+
    2023-01-31
    线程 自定义 数量
  • JAVA怎么控制线程池的线程数量
    Java中可以通过使用`ThreadPoolExecutor`类来控制线程池的线程数量。`ThreadPoolExecutor`提供...
    99+
    2023-10-11
    JAVA
  • Shell多线程操作及线程数控制方法教程
    本篇内容主要讲解“Shell多线程操作及线程数控制方法教程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Shell多线程操作及线程数控制方法教程”吧!需求为了更好的说明问题,我们结合例子讲解,假...
    99+
    2023-06-09
  • golang如何控制开启线程数
    在Go语言中,开启线程可以使用go关键字。但是,并发操作的线程数是由Go运行时系统自动管理的,开发者无法直接控制。Go运行时系统会根...
    99+
    2023-10-22
    golang
  • Python中如何创建多线程?
    目录一、python线程的模块1.thread和threading模块2. Queue模块3.注意模块的选择二、Threading模块三、通过Threading.Thread类来创建...
    99+
    2023-01-06
    python 创建多线程 Python多线程编程 python多线程开发
  • 如何进行Python线程的多线程展示
    如何进行Python线程的多线程展示,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。什么多线程?多线程,就是多个独立的运行单位,同时执行同样的事情。想想一下,文章发布后同时被...
    99+
    2023-06-22
  • WIN32控制台多线程中CRecordse::edit()出错
    在WIN32控制台多线程环境中,如果在多个线程中调用CRecordset的edit()方法,可能会导致出错。这是因为CRecords...
    99+
    2023-08-08
    WIN32
  • Python多线程编程,线程锁
    多线程threading 模块创建线程创建自己的线程类线程通信线程同步互斥方法线程锁@需要了解!!!   什么是线程? 线程也是一种多任务的编程方法,可以利用计算机多核资源完成程序的并发运行。 线程又被称为轻量级进程 ...
    99+
    2023-01-30
    线程 多线程 Python
  • python多线程————3、多线程间通
    1、共享变量 #通过共享变量 import time import threading url_list = [] def get_detail_html(): global url_list while True: ...
    99+
    2023-01-31
    多线程 python
  • 如何查看java线程池的线程数量
    要查看Java线程池的线程数量,可以使用`getPoolSize()`方法来获取线程池中当前的线程数量。以下是一个示例代码:```j...
    99+
    2023-08-24
    java
  • python多线程编程:如何优雅地关闭线程
    在并发编程中,我们可能会创建新线程,并在其中运行任务,可能由于一些原因,决定停止该线程。例如: 不再需要线程任务的结果了。应用程序正在关闭。线程执行可能已经出现了异常 关于python多线程编程知...
    99+
    2023-09-04
    python 开发语言
  • 如何在python中创建多线程
    今天就跟大家聊聊有关如何在python中创建多线程,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。python可以做什么Python是一种编程语言,内置了许多有效的工具,Python几...
    99+
    2023-06-14
  • 如何使用Python中的多线程编程
    如何使用Python中的多线程编程,需要具体代码示例引言:随着计算机技术的不断发展,多核处理器的普及以及大数据时代的到来,多线程编程变得越来越重要。多线程编程可以充分利用计算机的多个核心,加快程序的执行速度,提高系统的响应性能。Python...
    99+
    2023-10-22
    Python多线程编程
  • Python控制线程和函数超时处理
    目录前言一、Eventlet二、func-timeout1.安装2.使用前言 在我们使用线程池控制线程运行时,或者是写爬虫时不停发送request获取地址,都需要我们对运行的线程加以...
    99+
    2024-04-02
  • Python 限制线程的最大数量(Sem
    import threading import time sem=threading.Semaphore(4) #限制线程的最大数量为4个 def gothread(): with sem: #锁定线程的最大数量 ...
    99+
    2023-01-31
    线程 数量 Python
  • Python 多线程
      文章来源:https://www.runoob.com/python/python-multithreading.html 多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的...
    99+
    2023-01-31
    多线程 Python
  • python多线程
    Python 多线程 多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理。 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的...
    99+
    2023-01-30
    多线程 python
  • Python多线程如何使用
    今天小编给大家分享一下Python多线程如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一,实用方法线程之间执行是无序...
    99+
    2023-06-29
  • python—多线程
    一、多线程实例  线程时应用程序中工作的最小单位,python中提供了threading模块来对多线程操作,一般多核cpu采用多进程方式,单核才采用多线程方式  方法:  将要执行的方法threading.Thread作为参数传给构造方法(...
    99+
    2023-01-31
    多线程 python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作