Python的标准库提供了两个线程模块:_threadthreading_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

多线程

启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行,join()等待线程执行结束:

import time
import threading

def loop(num):
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < num:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)

def multiLoop(count, num):
ts = []
for idx in range(0, count):
name = 'LoopThread: %d' % idx
t = threading.Thread(target=loop, name=name, args=[num])
t.start()
ts.append(t)

for t in ts:
t.join()

print('thread %s is running...' % threading.current_thread().name)
multiLoop(5, 5)
print('thread %s ended.' % threading.current_thread().name)

上面的例子启动了5个线程,各自打印了5条log。

传参

多线程的调用比较简单,需要注意的是多线程执行时,需要考虑资源的分配问题,也就是需要适当的使用锁来避免程序因抢占资源而导致各种异常。

另外还有一个需要提的就是启动线程时的传参问题,上面已经给出了一个方案,使用 args 传递参数列表,从初始化原型可以看到还有另一个关键字参数 kwargs,可用于传递参数,两个方法都是可以的。

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

例如上面初始化线程的代码可以改成:

t = threading.Thread(target=loop, name=name, kwargs={'num': 5})

性能

就笔者在 Mac mini 和 Windows 上运行 Python 脚本的体验而言,Mac 可以甩 Windows 几条街,同样的 sql 统计语句,同样的 db 文件,Mac 上能在 1s 内处理完,Windows 却需要执行 4~5s,甚至更长。在设定线程个数时也需要根据不同平台有所区分,笔者会在 Mac 上开20个以上的线程,相对的在 Windows 上只开10个线程,因为开多了反而更慢。当然如果读者的 Windows 性能出色,当然可以根据运行环境自行调整合适的线程个数。