Python 的多线程与多进程_np

一、Python所有方向的学习路线

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

二、学习软件

工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

三、入门学习视频

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在参加 Kaggle 的 Understanding the Amazon from Space 比赛时,我试图对自己代码的各个部分进行加速。速度在 Kaggle 比赛中至关重要。高排名常常需要尝试数百种模型结构与超参组合,能在一个持续一分钟的 epoch 中省出 10 秒都是一个巨大的胜利。

让我吃惊的是,数据处理是最大的瓶颈。我用了 Numpy 的矩阵旋转、矩阵翻转、缩放及裁切等操作,在 CPU 上进行运算。Numpy 和 Pytorch 的 DataLoader 在某些情况中使用了并行处理。我同时会运行 3 到 5 个实验,每个实验都各自进行数据处理。但这种处理方式看起来效率不高,我希望知道我是否能用并行处理来加快所有实验的运行速度。

什么是并行处理?

简单来说就是在同一时刻做两件事情,也可以是在不同的 CPU 上分别运行代码,或者说当程序等待外部资源(文件加载、API 调用等)时把“浪费”的 CPU 周期充分利用起来提高效率。

下面的例子是一个“正常”的程序。它会使用单线程,依次进行下载一个 URL 列表的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面是一个同样的程序,不过使用了 2 个线程。它把 URL 列表分给不同的线程,处理速度几乎翻倍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果你对如何绘制以上图表感到好奇,可以参考源码,下面也简单介绍一下:

  1. 在你函数内部加上一个计时器,并返回函数执行的起始与结束时间
URLS = [url1, url2, url3, ...]
def download(url, base):
    start = time.time() - base
    resp = urlopen(url)
    stop = time.time() - base
    return start,stop
  1. 单线程程序的可视化如下:多次执行你的函数,并将多个开始结束的时间存储下来
results = [download(url, 1) for url in URLS]
  1. 将 [start, stop] 的结果数组进行转置,绘制柱状图
def visualize_runtimes(results):
    start,stop = np.array(results).T
    plt.barh(range(len(start)), stop-start, left=start)
    plt.grid(axis=’x’)
    plt.ylabel("Tasks")
    plt.xlabel("Seconds")

多线程的图表生成方式与此类似。Python 的并发库一样可以返回结果数组。

进程 vs 线程

一个进程就是一个程序的实例(比如 Jupyter notebook 或 Python 解释器)。进程启动线程(子进程)来处理一些子任务(比如按键、加载 HTML 页面、保存文件等)。线程存活于进程内部,线程间共享相同的内存空间。

举例:Microsoft Word
当你打开 Word 时,你其实就是创建了一个进程。当你开始打字时,进程启动了一些线程:一个线程专门用于获取键盘输入,一个线程用于显示文本,一个线程用于自动保存文件,还有一个线程用于拼写检查。在启动这些线程之后,Word 就能更好的利用空闲的 CPU 时间(等待键盘输入或文件加载的时间)让你有更高的工作效率。

进程

  • 由操作系统创建,以运行程序
  • 一个进程可以包括多个线程
  • 两个进程可以在 Python 程序中同时执行代码
  • 启动与终止进程需要花费更多的时间,因此用进程比用线程的开销更大
  • 由于进程不共享内存空间,因此进程间交换信息比线程间交换信息要慢很多。在 Python 中,用序列化数据结构(如数组)的方法进行信息交换会花费 IO 处理级别的时间。

线程

  • 线程是在进程内部的类似迷你进程的东西
  • 不同的线程共享同样的内存空间,可以高效地读写相同的变量
  • 两个线程不能在同一个 Python 程序中执行代码(有解决这个问题的方法*

CPU vs 核

CPU,或者说处理器,管理着计算机最基本的运算工作。CPU 有一个或着多个,可以让 CPU 同时执行代码。

如果只有一个核,那么对 CPU 密集型任务(比如循环、运算等)不会有速度的提升。操作系统需要在很小的时间片在不同的任务间来回切换调度。因此,做一些很琐碎的操作(比如下载一些图片)时,多任务处理反而会降低处理性能。这个现象的原因是在启动与维护多个任务时也有性能的开销。

Python 的 GIL 锁问题

CPython(python 的标准实现)有一个叫做 GIL(全局解释锁)的东西,会阻止在程序中同时执行两个线程。一些人非常不喜欢它,但也有一些人喜欢它。目前有一些解决它的方法,不过 Numpy 之类的库大都是通过执行外部 C 语言代码来绕过这种限制。

何时使用线程,何时使用进程?

  • 得益于多核与不存在 GIL,多进程可以加速 CPU 密集型的 Python 程序。
  • 多线程可以很好的处理 IO 任务或涉及外部系统的任务,因为线程可以将不同的工作高效地结合起来。而进程需要对结果进行序列化才能汇聚多个结果,这需要消耗额外的时间。
  • 由于 GIL 的存在,多线程对 CPU 密集的 Python 程序没有什么帮助。

*对于点积等某些运算,Numpy 绕过了 Python 的 GIL 锁,能够并行执行代码。

并行处理示例

Python 的 concurrent.futures 库用起来轻松愉快。你只需要简单的将函数、待处理的对象列表和并发的数量传给它即可。在下面几节中,我会以几种实验来演示何时使用线程何时使用进程。

def multithreading(func, args, 
                   workers):
    with ThreadPoolExecutor(workers) as ex:
        res = ex.map(func, args)
    return list(res)

def multiprocessing(func, args, 
                    workers):
    with ProcessPoolExecutor(work) as ex:
        res = ex.map(func, args)
    return list(res)

API 调用

对于 API 调用,多线程明显比串行处理与多进程速度要快很多。

def download(url):
    try:
        resp = urlopen(url)
    except Exception as e:
        print ('ERROR: %s' % e)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2 个线程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4 个线程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2 个进程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4 个进程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

IO 密集型任务

我传入了一个巨大的文本,以观测线程与进程的写入性能。线程效果较好,但多进程也让速度有所提升。

def io_heavy(text):
    f = open('output.txt', 'wt', encoding='utf-8')
    f.write(text)
    f.close()

(1)Python所有方向的学习路线(新版)

这是我花了几天的时间去把Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

最近我才对这些路线做了一下新的更新,知识体系更全面了。

在这里插入图片描述

(2)Python学习视频

包含了Python入门、爬虫、数据分析和web开发的学习视频,总共100多个,虽然没有那么全面,但是对于入门来说是没问题的,学完这些之后,你可以按照我上面的学习路线去网上找其他的知识资源进行进阶。

在这里插入图片描述

(3)100多个练手项目

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。

在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

项目去练练。

在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

### 回答1: 很抱歉,我之前回答的还是这个问题。以下是一个稍微复杂一点的 Python 多线程实例,可以用来计算一个矩阵的乘积: ```python import threading import numpy as np # 定义矩阵的大小 MATRIX_SIZE = 1000 # 初始化两个随机矩阵 matrix_a = np.random.rand(MATRIX_SIZE, MATRIX_SIZE) matrix_b = np.random.rand(MATRIX_SIZE, MATRIX_SIZE) # 初始化结果矩阵 result_matrix = np.zeros((MATRIX_SIZE, MATRIX_SIZE)) # 定义一个函数,用来计算矩阵的乘积 def multiply(start, end): for i in range(start, end): for j in range(MATRIX_SIZE): for k in range(MATRIX_SIZE): result_matrix[i][j] += matrix_a[i][k] * matrix_b[k][j] # 定义线程数量 NUM_THREADS = 4 # 计算每个线程需要计算的行数 rows_per_thread = MATRIX_SIZE // NUM_THREADS # 创建线程列表 threads = [] # 启动线程 for i in range(NUM_THREADS): start = i * rows_per_thread end = (i + 1) * rows_per_thread t = threading.Thread(target=multiply, args=(start, end)) threads.append(t) t.start() # 等待所有线程结束 for t in threads: t.join() # 检查结果是否正确 expected_result = np.dot(matrix_a, matrix_b) assert np.array_equal(result_matrix, expected_result) # 输出 Done 表示程序执行完毕 print("Done") ``` 这个例子首先初始化两个随机矩阵,然后定义一个函数 multiply,用来计算这两个矩阵的乘积。然后,它将矩阵分成若干个部分,每个部分分配给一个线程来计算。最后,它等待所有线程结束,检查结果是否正确,并输出 Done 表示程序执行完毕。 希望这个例子能够帮助您更好地理解 Python 多线程的使用方法。 ### 回答2: Python多线程实例是指在Python编程语言中使用多线程进行并发编程的实例。在Python中,可以使用内置的threading模块来创建和管理多线程。 通过创建多线程,可以在程序中同时执行多个任务。这对于需要同时处理多个任务的情况非常有用。例如,在下载大文件时,可以使用多线程同时下载多个文件,加快下载速度。此外,多线程还可以用于处理网络请求、图像处理、数据处理等耗时操作,提高程序的运行效率。 使用Python多线程的主要步骤如下: 1. 导入threading模块。 ``` import threading ``` 2. 创建一个线程对象,可以通过继承threading.Thread类或使用threading.Thread()函数创建。 ``` class MyThread(threading.Thread): def __init__(self, name): super().__init__() self.name = name def run(self): # 线程执行的代码 print("Hello, " + self.name) thread1 = MyThread("Thread 1") thread2 = threading.Thread(target=func, args=("Thread 2",)) ``` 3. 启动线程。 ``` thread1.start() thread2.start() ``` 4. 等待线程结束。 ``` thread1.join() thread2.join() ``` 以上代码演示了两种创建多线程的方法:1)继承threading.Thread类,重写run方法;2)使用函数作为线程的执行内容。线程的启动调用start()方法,等待线程结束使用join()方法。 需要注意的是,Python多线程的并发程度受到全局解释器锁(GIL)的限制,因此对于计算密集型的任务,多线程并不能发挥出多核的优势。如果需要发挥多核性能,可以考虑使用多进程编程。 总之,Python多线程实例能够提高程序的并发处理能力,适用于需要同时处理多个任务的场景。通过合理设计线程的数量和任务分配,可以提高程序的性能和效率。 ### 回答3: Python多线程实例是指通过使用多线程的技术来提高Python程序的运行效率和性能。在Python中,我们可以使用threading模块来实现多线程多线程技术可以同时执行多个任务,提高程序的运行速度。在Python中,我们可以通过创建Thread对象并调用start()方法来启动一个线程。下面是一个简单的例子: import threading def print_numbers(): for i in range(1, 11): print(i) def print_letters(): for letter in ['A', 'B', 'C', 'D', 'E']: print(letter) # 创建两个线程 t1 = threading.Thread(target=print_numbers) t2 = threading.Thread(target=print_letters) # 启动两个线程 t1.start() t2.start() # 等待两个线程结束 t1.join() t2.join() # 主线程继续执行 print("主线程结束") 以上代码中,我们创建了两个线程,分别执行print_numbers()和print_letters()函数。通过调用start()方法启动线程,并通过join()方法等待两个线程执行完毕。最后,主线程继续执行并打印出一段文字。 需要注意的是,多线程并不一定能提高程序的运行速度,因为在Python中,全局解释器锁(Global Interpreter Lock,GIL)会限制同一时间只能有一个线程执行Python字节码。因此,在CPU密集型任务中,多线程并不能真正实现并行计算。但是,在IO密集型任务中,多线程能够提高程序的运行效率。 总结起来,Python多线程实例可以通过使用threading模块来实现。多线程能够提高IO密集型任务的运行效率,但在CPU密集型任务中并不能真正实现并行计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值