python用多线程可以快几倍_真正的python 多线程!一个修饰符让你的多线程和C语言一样快...

> Python 多线程因为GIL的存在,导致其速度比单线程还要慢。但是近期我发现了一个相当好用的库,这个库只需要增加一个修饰符就可以使原生的python多线程实现真正意义上的并发。本文将和大家一起回顾下GIL对于多线程的影响,以及了解通过一个修饰符就可以实现和C++一样的多线程。

## GIL的定义

GIL的全称是global interpreter lock,官方的定义如下:

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

从官方的解释来看,这个全局锁是用来防止多线程同时执行底层计算代码的。之所以这么做,是因为底层库Cpython,在内存管理这块是线程不安全的。

## GIL有好处吗

对GIL的第一印象是这东西限制了多线程并发,对python而言是个弊大于利的存在。但是从stackoverflow上的讨论来看,这个存在还是相当有必要的。

- 增加了单线程的运行速度

- 可以更方便地整合一些线程不安全的C语言库到python里面去

首先单线程的运行速度更快了,因为有这个全局锁的存在,在执行单线程计算的时候不需要再额外增加锁,减少了不必要的开支。第二个则是可以更好地整合用C语言所写的python库。现在其实挺多用C语言写好底层计算然后封装提供python接口的,比如数据处理领域的pandas库,人工智能领域的计算框架Tensorflow或者pytorch,他们的底层计算都是用C语言写的。由于这个全局锁的存在,我们可以更方便(安全)地把这些C语言的计算库整合成一个python包,对外提供python接口。

## GIL对性能的影响大吗

对于需要做大量计算的任务而言,影响是相当大的。我们先来看一段单线程代码:

```python

class A(object):

def run(self):

ans = 0

for i in range(100000000):

ans += i

a = A()

for _ in range(5):

a.run()

```

以上这段代码是跑5次计算,每次计算是从1累加到1千万,跑这段代码需要17.46s。

紧接着,我们用python的多线程库来实现一个多线程计算:

```python

import threading

class A(object):

def run(self):

ans = 0

for i in range(100000000):

ans += i

threads = []

for _ in range(5):

a = A()

th = threading.Thread(target=a.run)

th.start()

threads.append(th)

for th in threads:

th.join()

```

这里我们启动了5个线程同时计算,然后我们又测试下时间: **41.35**秒!!!这个时候GIL的问题就体现出来了,我们通过多线程来实现并发,结果比单线程慢了2倍多。

### 一个神奇的修饰符

话不多说,我们先来看下代码。以下这段代码和上面的多线程代码几乎一样。但是我们要注意到,在类A的定义上面,我们增加了一个修饰符*@parl.remote_class*。

```python

import threading

import parl

@parl.remote_class

class A(object):

def run(self):

ans = 0

for i in range(100000000):

ans += i

threads = []

parl.connect("localhost:6006")

for _ in range(5):

a = A()

th = threading.Thread(target=a.run)

th.start()

threads.append(th)

for th in threads:

th.join()

```

现在我们来看下计算时间:**3.74秒**!!!相比于单线程的17.46s,这里只用了接近1/5的时间(因为我们开了5个线程)。这里是我觉得比较神奇的地方,并没有做太多的改动,只是在我的单线程类上面增加了一个修饰符,然后用原生的python多线程继续跑代码就变得相当快了。

### 完整的使用说明:

1. 安装这个库:

```shell

pip install --upgrade git+https://github.com/PaddlePaddle/PARL.git

```

2. 在本地通过命令启动一个并发服务(只需要启动一次)

```shell

xparl start --port 6006

```

3. 写代码的时候通过修饰符修饰你要并发的类@parl.remote。

这里需要注意的是只有经过这个修饰符修饰的类才可以实现并发。

4. 在代码最开始的时候通过parl.connect('localhost:6006')来初始化这个包。

最后贴下这个库的使用文档:

https://parl.readthedocs.io/en/latest/parallel_training/setup.html

源码在这里:

https://github.com/PaddlePaddle/PARL/tree/develop/parl/remote

后续会继续研究源码,看下是怎么做到一个修饰符就能加速的。大家如果读过了源码可以一起讨论下:)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值