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