Python之多线程与多进程

简介: Python之多线程与多进程

Python之多线程与多进程
最近一直在学习爬虫的知识,遇到了很多坎坷也同时学到了很多以往从未接触到的知识和领域。我想,学习的点滴进步就是在遇到一个一个问题然后又一个一个解决中积累起来的吧,乐趣也在于此,引用一句话“谁学谁知道啊”😀。

-----史蒂夫•乔布斯的名言:生活不会突变,你要做的只是耐心和积累。人这一辈子没法做太多事情,所以每一件都要做得精彩绝伦。

下面进入正题,目前爬虫技术已经应用非常广泛。当爬取数据量非常大时,就要考虑如何提高爬虫效率。如果使用单线程的爬虫,爬取数据的速度是非常慢的。那么解决的办法就是使用Python中的多线程和多进程,这样就可以实现同时完成多项工作,提高执行效率。

1.1 进程与线程
1.1.1 什么是进程
在了解进程之前,我们有必要知道多任务的概念。多任务,顾名思义,就是指操作系统能够执行多个任务。像我们使用Windows和Linux操作系统可以同时看电影、打字、查看网页等等,这就是操作系统在执行多任务,而每一个任务就是一个进程。我以Mac系统为例,查看进程

# 执行命令,查看进程号
ps -ef | grep 进程名
liuxiaowei@MacBookAir ~ % ps -ef | grep 3306
501 5102 854   0 5:45PM ttys000     0:00.00 grep 3306
# 执行命令,查看进程
liuxiaowei@MacBookAir ~ $sudo lsof -i

image.png

上图中显示的进程只是一部分截图,包括电脑运行的各种应用程序进程(Process)。

1.1.2 什么是线程
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程可以并发多个线程,每条线程并行执行不同的任务。这可以打个比方:视频播放这个进程中,显示视频用一个线程,播放音频是一个线程。只有两个线程同时工作,我们才能正常观看画面和声音同步的视频。进程好比一列火车,而线程好比车厢,线程需要在进程下进行,就好比单独的车厢无法行驶一样。一个进程可以包含很多线程,就像火车有很多车厢一样。

1.2创建线程
Python语言内置多线程支持,它的标准库提供了两个模块:_thread和threading, _thread是低级模块,threading是高级模块,对 _thread进行封装。绝大多数情况我们只使用threading这个高级模块。

1.2.1 使用threading模块创建线程

# Pycharm环境python3.9
#_*_coding:utf-8_*_
# 作者      :liuxiaowei
# 创建时间   :1/15/22 6:42 PM
# 文件      :threading创建线程.py
# IDE      :PyCharm

import threading,time

def process():
    for i in range(5):
        time.sleep(1)
        print("thread name is %s" % threading.current_thread().name)
if __name__ == "__main__":
    print("------主线程开始-----")

# 创建4个线程,存入列表
    threads = [threading.Thread(target=process) for i in range(4)]
    for t in threads:
        t.start()       # 开启线程
    for t in threads:
        t.join()        # 等待子线程结束
    print("-----主线程结束-----")

上述代码中,创建了4个线程,然后分别用for循环执行4次start()和join()方法。每个子线程分别执行5次。运行结果如下:
image.png

注:从中可以看出线程的执行顺序是不确定的

1.2.2 使用Thread子类创建线程

# Thread线程累也可以通过定义一个继承Thread线程类的子类来创建线程SubThread,并定义一个方法(),实例化SubThread类创建2个线程,并且调用start()方法开启线程,会自动调用run()方法
# Thread线程累也可以通过定义一个继承Thread线程类的子类来创建线程SubThread,并定义一个方法(),实例化SubThread类创建2个线程,并且调用start()方法开启线程,会自动调用run()方法
#_*_coding:utf-8_*_
# 作者      :liuxiaowei
# 创建时间   :1/15/22 7:51 PM
# 文件      :Thread子类创建线程.py
# IDE      :PyCharm

import threading
import time
class SubThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "子线程" + self.name + "执行,i=" + str(i)  # name属性中保存的是当前线程的名字
            print(msg)

if __name__ == "__main__":
    print("------主线程------")
    t1 = SubThread()                       # 创建子线程t1
    t2 = SubThread()                       # 创建子线程t2
    t1.start()                             # 启动子线程t1
    t2.start()                             # 启动子线程t2
    t1.join()                              # 等待子线程t1
    t2.join()                              # 等待子线程t2
    print("------主线程结束------")
![image.png](https://ucc.alicdn.com/pic/developer-ecology/y53t27ieobnpw_e886594a664c44b4a92f7e1851c3c139.png)




1.3 创建进程
1.3.1创建进程的常用方式
1.3.1.1 使用multiprocessing模块创建进程
```js
# 实例化进程
from multiprocessing import Process             # 导入模块

# 执行子进程代码
def test(interval):
    print('我是子进程')

# 执行主程序
def main():
    print("主进程开始")
    p = Process(target = test, args = (1, ))            # 实例化Process进程类
    p.start()                                           # 启动子进程
    print('主进程结束')
if __name__ == '__main__':
        main()

image.png

1.3.1.2 使用Process子类创建进程

#_*_coding:utf-8_*_
# 作者      :liuxiaowei
# 创建时间   :1/15/22 9:03 PM
# 文件      :使用Process子类创建进程.py
# IDE      :PyCharm

from multiprocessing import Process
import time
import os

# 继承Process类
class SubProcess(Process):
    # 由于Process类本身也有__init__初始化方法,这个子类相当于重写类子父类的这个方法
def __init__(self, interval, name = ''):
        Process.__init__(self)              # 调用Process父类的初始化方法
        self.interval = interval            # 接收参数interval

        if name:                            # 判断传递的参数name是否存在
            self.name = name                # 如果传递参数name,则为了子进程创建name属性,否则使用默认属性
    def run(self):
        print("子进程(%s) 开始执行, 父进程为(%s) "%(os.getpid(),os.getppid()))
        t_start = time.time()
        time.sleep(self.interval)
        t_stop = time.time()
        print("子进程(%s)执行结束,耗时%0.2f秒"%(os.getpid(),t_stop-t_start))

if __name__ == "__main__":
    print("-----父进程开始执行------")
    print("父进程PID: %s" %os.getpid())       # 输出当前程序的ID
    p1 = SubProcess(interval=1, name='mrsoft')
    p2 = SubProcess(interval=2)
    # 对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法
    # 所以这里不会执行p1.run()
    p1.start()                      # 启动进程p1
    p2.start()                      # 启动进程p2
    # 输出p1和p2进程的执行状态,如果真正进行,返回True, 否则返回Fasle
    print("p1.is_alive=%s"%p1.is_alive())
    print("p2.is_alive=%s"%p2.is_alive())

    # 输出p1和p2进程的别名和PID
    print("p1.name=%s"%p1.name)
    print("p1.pid=%s"%p1.pid)
    print("p2.name=%s"%p2.name)
    print("p2.pid=%s"%p2.pid)
    print("-------等待子进程-------")
    p1.join()               # 等待p1进程结束
    p2.join()               # 等待p2进程结束
    print("-------父进程执行结束-------")

image.png

1.3.1.3 使用进程池Pool创建进程

定义一个进程池,设置最大进程数为3,然后使用非阻塞方式执行10个任务,查看每个进程执行的任务

#_*_coding:utf-8_*_
# 作者      :liuxiaowei
# 创建时间   :1/15/22 9:52 PM
# 文件      :使用进程池创建进程.py
# IDE      :PyCharm


from multiprocessing import Pool
import os, time


def task(name):
    print('子进程 (%s)执行task %s ...' %(os.getpid(), name))
    time.sleep(1)               # 休眠1if __name__ == '__main__':
    print('父进程(%s) .' %os.getpid())
    p = Pool(3)                 # 定义一个进程池,最大进程数3
    for i in range(10):         # 从0开始循环10次
        p.apply_async(task, args=(i,))  # 使用非阻塞方式调用task()函数
    print('等待所有子进程结束...')
    p.close()                   # 关闭进程池,关闭后p不再接收新的请求
    p.join()                    # 等待子进程结束
    print('所有子进程结束.')

image.png

关于进程和线程还有很多方法和属性,还有进程间通信,线程间通信等等。这就是我的一点学习经历,整理出来跟大家分享。如果文中出现了错误,请大家帮忙斧正,不吝赐教。欢迎转发、收藏、点赞。

相关文章
|
8天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
12天前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
9天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
21 1
|
15天前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
30 2
|
16天前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
20天前
|
Java Unix 调度
python多线程!
本文介绍了线程的基本概念、多线程技术、线程的创建与管理、线程间的通信与同步机制,以及线程池和队列模块的使用。文章详细讲解了如何使用 `_thread` 和 `threading` 模块创建和管理线程,介绍了线程锁 `Lock` 的作用和使用方法,解决了多线程环境下的数据共享问题。此外,还介绍了 `Timer` 定时器和 `ThreadPoolExecutor` 线程池的使用,最后通过一个具体的案例展示了如何使用多线程爬取电影票房数据。文章还对比了进程和线程的优缺点,并讨论了计算密集型和IO密集型任务的适用场景。
38 4
|
20天前
|
调度 iOS开发 MacOS
python多进程一文够了!!!
本文介绍了高效编程中的多任务原理及其在Python中的实现。主要内容包括多任务的概念、单核和多核CPU的多任务实现、并发与并行的区别、多任务的实现方式(多进程、多线程、协程等)。详细讲解了进程的概念、使用方法、全局变量在多个子进程中的共享问题、启动大量子进程的方法、进程间通信(队列、字典、列表共享)、生产者消费者模型的实现,以及一个实际案例——抓取斗图网站的图片。通过这些内容,读者可以深入理解多任务编程的原理和实践技巧。
43 1
|
21天前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。
|
4月前
|
安全 Python
告别低效编程!Python线程与进程并发技术详解,让你的代码飞起来!
【7月更文挑战第9天】Python并发编程提升效率:**理解并发与并行,线程借助`threading`模块处理IO密集型任务,受限于GIL;进程用`multiprocessing`实现并行,绕过GIL限制。示例展示线程和进程创建及同步。选择合适模型,注意线程安全,利用多核,优化性能,实现高效并发编程。
74 3
|
27天前
|
Python
Python中的多线程与多进程
本文将探讨Python中多线程和多进程的基本概念、使用场景以及实现方式。通过对比分析,我们将了解何时使用多线程或多进程更为合适,并提供一些实用的代码示例来帮助读者更好地理解这两种并发编程技术。