Intro
这篇博客主要介绍 python3 自带的并行库,算是对 17. Concurrent Execution 的简介。并提供一些例子。
CSAPP (Computer System A Programmer’s Prospective) 上对并行(concurrent),进程(process),线程(thread)有详细的介绍,其具体内涵可以 Google 。总之,线程和进程的区别在于线程之间共享同一套虚拟内存地址,而不同的进程所用的虚拟内存地址不同,因此需要特殊的数据交换机制;除此之外,可以把线程看作是轻量级的(light-weighted)进程。
共享一套虚拟内存地址既是多线程的优势,也是它的劣势,一方面,数据修改变得方便,另一方面,不同线程对同一个变量先后进行操作所产生的竞争冒险总是会造成令人费解的错误。因此,如何处理这个问题就变得很重要。
Multi-thread
多线程
Python3 中和多线程相关的库有 threading
, queue
等,而 dummy_threading
, _thread
, _dummy_thread
提供了一些支持。
_thread
_thread
库相对偏底层,功能也相对简单(创建一个新的线程并运行相关的函数,设置 lock
)
import _thread
# Starting a new thread
_thread.start_new_thread(function, args[, kwargs])
同时 _thread
中也提供一个 Lock
类,它可以用来处理线程之间的同步问题。通过 import threading
也可以使用这个类,因此这个类的功能在 threading
中介绍。
链接
threading
threading
是 python3 中比较 high-level 的并行库,能够实现的功能也比较多,其中有 Thread
, Lock
等类。
class Thread
docs.python 中提供了对它的介绍。这是库中实现多线程操作的核心的一个类。
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
- group 的功能尚未实现
- target 是可以是一个函数或者一个定义了
__call__
的类对象。在 python 中,凡是定义了__call__
的类A,其实例a都可以使用:a()
执行__call__
中的内容。 - name 是这个线程的名字,没有别的用处
- args 是一个 tuple ,这里是一般的参数
- kwargs 是一个 dict ,存放关键字参数,例如:
{'a': 1}
Thread
的三个成员函数如下:
start()
启动线程run()
Thread 在这个函数中运行 target ,用户可以重写(overwrite)这个函数,例如可以加上一些别的操作join()
等待线程结束,这个很重要,一个后台运行的线程在不经意间修改了一个变量会带来很奇怪的错误。
一个简单的例子如下:
from threading import Thread
def one():
print(1)
def two():
print(2)
class One():
def __call__(self, kw=None):
one()
print(kw)
class Two():
def __call__(self, kw=None):
two()
print(kw)
def multithread_run(lst: list) -> None:
threads = []
size = len(lst)
for i in range(size):
threads.append(Thread(target=lst[i]['target'], kwargs=lst[i]['kwargs']))
for t in threads:
t.start()
for t in threads:
t.join()
for t in threads:
print(t.is_alive())
if __name__ == '__main__':
lst = [
{'target': one, 'kwargs': None},
{'target': two, 'kwargs': None}
]
Lst = [{"target": o, "kwargs": {"kw": "one"}},
{"target": t, "kwargs": {"kw": "two"}},
]
multithread_run(lst)
print('-' * 32)
multithread_run(Lst)
另外一些简单的例子见 tutorialspoint
Lock
在了解 lock
的之前,我们需要先来看一看多线程中的竞争冒险,考虑如下一个程序:
ThreadPool
python 中提供线程池的功能。线程池中保留一定数量的线程资源,当某个新的线程需要资源的时候,为其分配,当该线程结束,就将资源还给线程池。这样的好处是免去了每次申请和释放线程资源的开销。
stackoverflow 的这个链接 描述了线程池,import 命令如下:
from concurrent.futures import ThreadPoolExecutor
Reference
[1]. docs.python
[2]. turorialspoint.com
[3]. Bryant and O’Hallaron. Computer System: A Programmer’s Perspective