多进程和多线程在编程中是一个重要的特性,本文主要讲解在python中如何实现多线程和多进程。
多线程
python中多线程速度慢,不建议使用!
python中的多线程机制不够完善,用起来效果很鸡肋。以我的实现为例,在不使用多线程的情况下运行一次需要5s左右,使用多线程之后反而需要14s之久。这显然是不可接受的,所以不推荐在python中使用多线程,如果真的需要并发,建议使用多进程代替。
python中的多线程速度慢究其原因在于python中的Global
Interpreter
Lock(GIL),线程只有获得GIL才可运行,导致的结果是每次最多只有一个线程在运行。GIL导致运行慢的原因不在本文讨论范围,感兴趣可自行搜索。
不过python中的多进程基于多线程实现,两者的接口使用方式几乎是一样的,所以本文还是对多线程实现做出讲解。
实现方法
python中多线程通过threading
库实现,创建线程有两种方法,不过根本上都是需要用到threading.Thread
类。
方法1:自定义线程的运行内容
这种方法需要自己提前以函数的形式定义好要在线程中运行的程序。为直观起见,这里我只使用了两个线程,两者均运行同一个程序函数do_something
。此外,我这里将输出用日志打印,这样可以显示运行时间,便于理解线程的执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import time import threading
import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%H:%M:%S')
def do_something(name): logging.info(name + ' is playing toy.') time.sleep(1) logging.info(name + ' leaves.')
if __name__ == '__main__': children = ['Tom', 'John'] threads = [threading.Thread(target=do_something, args=(child,)) for child in children]
for thread in threads: thread.start() for thread in threads: thread.join()
|
运行上述代码,我得到的输出是:
1 2 3 4
| 18:50:31 Tom have access to toy: beer 18:50:31 John have access to toy: beer 18:50:32 Tom leaves! 18:50:32 John leaves!
|
可见,两个线程同时开始运行,并在执行结束后分别退出。
方法2:自定义线程
相比于方法1以函数的形式定义线程要运行的程序,方法2直接定义一个要运行的线程,该线程需继承threading.Thread
类,并重写其run
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import time import threading
import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%H:%M:%S')
class DoSomething(threading.Thread): def __init__(self, name, *args, **kargs): super().__init__(*args, **kargs) self.name = name
def run(self): '''运行程序写在这里''' logging.info(self.name + ' is playing toy.') time.sleep(1) logging.info(self.name + ' leaves.')
if __name__ == '__main__': children = ['Tom', 'John'] threads = [DoSomething(name=child) for child in children]
for thread in threads: thread.start()
for thread in threads: thread.join()
|
运行该程序输出和方法1类似。
共享资源
在运行多线程的时候经常需要多个线程共享资源,要实现这一点只需对上边的程序稍微改进即可,这里以方法1为例说明,我额外创建了一个Shared
类用于多线程共享。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import time import threading import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%H:%M:%S')
class Shared(object): toy = 'beer'
def do_something(resource, name): logging.info(name + ' have access to toy: ' + resource.toy) time.sleep(1) logging.info(name + ' leaves!')
if __name__ == '__main__': resource = Shared()
children = ['Tom', 'John'] threads = [threading.Thread(target=do_something, args=(resource, child)) for child in children]
for thread in threads: thread.start()
for thread in threads: thread.join()
|
使用方法2共享资源也是同理。
多进程
相对于python中多线程存在的硬伤,多进程则靠谱很多。python任何希望用多线程实现的程序都可以用多进程代替,而且多进程提供了和多线程非常相似的API接口,迁移起来非常方便。
python中多进程通过multiprocessing
库实现,实现的类是multiprocessing.Process
。由于和多线程接口一致,多进程也有两种实现方式,而且多进程也可以像多线程一样共享资源。
以方法1为例,要想将多线程改写为多进程,只需要导入multiprocessing
库并将threading.Thread
替换为multiprocessing.Process
即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import time # 导入多进程库 import multiprocessing
import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%H:%M:%S')
class Shared(object): toy = 'beer'
def do_something(resource, name): logging.info(name + ' have access to toy: ' + resource.toy) time.sleep(1) logging.info(name + ' leaves!')
if __name__ == '__main__': resource = Shared()
children = ['Tom', 'John'] # 替换 threads = [multiprocessing.Process(target=do_something, args=(resource, child)) for child in children]
for thread in threads: thread.start()
for thread in threads: thread.join()
|