上一篇博客介绍了Python并发编程的基本知识,感兴趣的可以到专利找到上一篇文章,本文主要介绍如何开启多线程,以及多线程在提升效率的同时会存在哪些问题
Python通过标准库的 threading
模块来管理线程。这个模块提供了很多不错的特性,让线程变得无比简单,打开threading.py文件,可以到如下类或者方法
线程模块常用的主要组件有,Thread、Event、Lock、RLock、Semaphore、Timer等等,他们可以用来开启线程、保证线程间的安全、线程间的通信等等
使用线程最简单的一个方法是,用一个目标函数实例化一个Thread然后调用 start()
方法启动它。示例如下
import threading
def function(i):
print ("function called by thread %i\n" % i)
return
threads = []
for i in range(5):
t = threading.Thread(target=function , args=(i, ))
threads.append(t)
t.start()
t.join()
上述代码就可以开启5个线程执行function函数
Thread()参数解释如下
-
group
: 一般设置为None
,这是为以后的一些特性预留的 -
target
: 当线程启动的时候要执行的函数 -
name
: 线程的名字,默认会分配一个唯一名字Thread-N
-
args
: 传递给target
的参数,要使用tuple类型 -
kwargs
: 同上,使用字典类型dict
Thread类定义的常用方法如下
-
start()
: 启动线程,并执行run()
方法。 -
run()
: 线程启动后执行的方法,可以在子类中重写。 -
join([timeout])
: 等待线程结束,可选参数timeout
指定最长等待时间。 -
is_alive()
: 判断线程是否仍然存活。 -
name
: 线程名称的属性,可以在实例化时指定或修改。 -
setDaemon(daemonic)
: 设置线程是否为守护线程,默认为 False。守护线程会在主线程结束时自动退出。 -
getName()
: 获取线程名称。 -
setName(name)
: 设置线程名称。
多个线程操作同一个资源,并且至少有一个可以改变数据,又没有同步机制的条件下,就会产生竞争条件,可能会导致执行无效代码、bug、或异常行为。
这时候可以用Lock来保持线程同步,例如下列代码,在下面的代码中,我们有两个函数: increment()
和 decrement()
。第一个函数对共享资源执行加1的操作,另一个函数执行减1.两个函数分别使用线程封装。除此之外,每一个函数都有一个循环重复执行操作。我们想要保证,通过对共享资源的管理,执行结果是共享资源最后等于初始值0.
# -*- coding: utf-8 -*-
import threading
shared_resource_with_lock = 0
shared_resource_with_no_lock = 0
COUNT = 10000000
shared_resource_lock = threading.Lock()
# 有锁的情况
def increment_with_lock():
global shared_resource_with_lock
for i in range(COUNT):
shared_resource_lock.acquire()
shared_resource_with_lock += 1
shared_resource_lock.release()
def decrement_with_lock():
global shared_resource_with_lock
for i in range(COUNT):
shared_resource_lock.acquire()
shared_resource_with_lock -= 1
shared_resource_lock.release()
# 没有锁的情况
def increment_without_lock():
global shared_resource_with_no_lock
for i in range(COUNT):
shared_resource_with_no_lock += 1
def decrement_without_lock():
global shared_resource_with_no_lock
for i in range(COUNT):
shared_resource_with_no_lock -= 1
if __name__ == "__main__":
t1 = threading.Thread(target=increment_with_lock)
t2 = threading.Thread(target=decrement_with_lock)
t3 = threading.Thread(target=increment_without_lock)
t4 = threading.Thread(target=decrement_without_lock)
t1.start()
t2.start()
t3.start()
t4.start()
t1.join()
t2.join()
t3.join()
t4.join()
print ("the value of shared variable with lock management is %s" % shared_resource_with_lock)
print ("the value of shared variable with race condition is %s" % shared_resource_with_no_lock)
输出
the value of shared variable with lock management is 0
the value of shared variable with race condition is -242686
可以看到未加锁的程序出现了错误,如果你运行了示例代码没有出错,可以把COUNT值调大试试
但加锁有什么缺点呢,第一会消耗资源,第二如果程序中有多个锁存在,可能会造成死锁,导致程序一直卡住,我们设有两个并发的线程( 线程A 和 线程B ),需要 资源1 和 资源2 .假设 线程A 需要 资源1 , 线程B 需要 资源2 .在这种情况下,两个线程都使用各自的锁,目前为止没有冲突。现在假设,在双方释放锁之前, 线程A 需要 资源2 的锁, 线程B 需要 资源1 的锁,没有资源线程不会继续执行。这就出现了死锁问题。
下篇文章介绍如何解决多线程的死锁问题