Python之多线程开发(一)
使用Lock和RLock对象
如果多个线程共同对某个数据进行修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步修改,在Python程序中,使用对象Lock和RLock
可以实现简单的线程同步功能,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
多线程的优势在于可以同时运行多个任务(感觉上是这样),但是当线程需要共享数据时,可能存在数据不同步的问题。以下代码演示了使用RLock实现线程同步。
import threading
import time
class mt(threading.Thread):
def run(self):
global x
lock.acquire()
for i in range(5):
x += 10
time.sleep(1)
print(x)
lock.release()
x = 0
lock = threading.RLock()
def main():
thrs = []
for item in range(8):
thrs.append(mt())
for item in thrs:
item.start()
if __name__ == '__main__':
main()
上述实例中,自定义了一个带锁访问全局变量x的线程类mt,在主函数main()中初始化了8个线程来修改x,在同一时间只能由一个线程对x进行操作,执行效果如下:
50,100,150....。在python程序中,要想让可变对象安全的用在多线程环境下,可以利用threading中的Lock对象来解决,以下代码演示了Lock对临界区加锁的过程。
import threading
class ShareCounter:
def __init__(self, initial_value=0):
self._value = initial_value
self._value_lock = threading.Lock()
def incr(self, delta=1):
with self._value_lock:
self._value += delta
def decr(self, delta=1):
with self._value_lock:
self._value -= delta
def test(c):
for n in range(10000):
c.incr()
for n in range(10000):
c.decr()
if __name__ == '__main__':
c = ShareCounter()
t1 = threading.Thread(target=test, args=(c,))
t2 = threading.Thread(target=test, args=(c,))
t3 = threading.Thread(target=test, args=(c,))
t1.start()
t2.start()
t3.start()
print('Runing test')
t1.join()
t2.join()
t3.join()
assert c._value == 0
print('Locks good!',c._value) # 0
上述代码中,当使用with语句时,Lock对象可确保产生互斥的行为,也就是说在同一时间只运行一个线程执行with里面的语句。
使用Condition对象
在Python中,使用Condition对象可以在某些事件触发或达到特定条件下后才能处理数据,提供的Condition对象目的就是实现对复杂线程同步问题的支持,
Condition通常和一个锁关联,当需要在多个Condition中共享一个锁时,可以传递一个Lock/RLock实例,否则它会自动生成一把锁。下面代码会使用
Conditon实现一个捉迷藏游戏:1.当游戏开始之后,Seeker先把眼睛蒙上,统治Hider;2.Hider接收到通知后,将自己藏起来,再通知Seeker可以找了。
import threading, time
class Hider(threading.Thread):
def __init__(self,cond,name):
super(Hider,self).__init__() # 需要先执行父类的初始化函数,否则name会被覆盖
self.cond = cond
self.name = name
def run(self):
time.sleep(1) # 确保Seeker先运行
self.cond.acquire() # 3,获得锁,执行下面的操作
print("我已经把眼睛闭上了!")
self.cond.notify() # 通知另一个解锁,自己挂起
self.cond.wait()
print(self.name,": 我已经找到你了")
self.cond.notify()
self.cond.release()
print(self.name,': 我赢了')
class Seeker(threading.Thread):
def __init__(self,cond,name):
super(Seeker,self).__init__() # 需要先执行父类的初始化函数,否则name会被覆盖
self.cond = cond
self.name = name
def run(self):
self.cond.acquire() # 1, 获得锁
self.cond.wait() # 2,释放锁的占用,同时线程挂起,知道notify() 并重新占用锁
print(self.name,": 我已经藏好了,你快点找我把")
self.cond.notify()
self.cond.wait()
self.cond.release()
print(self.name,": 被你找到了,哎")
cond = threading.Condition()
seeker = Seeker(cond, 'seeker')
hider = Hider(cond, 'hider')
seeker.start()
hider.start()
"""
我已经把眼睛闭上了!
seeker : 我已经藏好了,你快点找我把
hider : 我已经找到你了
hider : 我赢了
seeker : 被你找到了,哎"""
使用Semaphore和BoundedSemaphore对象
Python中,可以使用Semaphore和BoundedSemaphore来控制多线程信号系统中的计数器。
1、Semaphore: 类threading.Semaphore是一个信号机,控制着对公共资源或者临界区的访问,信号机维护着一个计数器,指定可同时访问资源或者进入临界区的线程数,每次
有一个线程获得信号机,计数器为-1,如计数器为0,其他线程停止访问。
import threading, time
def fun(semaphore,num):
semaphore.acquire()
print("降龙十八掌,发出%d掌"%num)
time.sleep(3)
semaphore.release()
if __name__ =='__main__':
semaphore = threading.Semaphore(2)
for num in range(4):
t = threading.Thread(target=fun, args=(semaphore,num))
t.start()
执行后,线程0,1是同时运行的,2,3是3秒后运行。
2、BoundedSemaphore: 它会检查内部计数器的值,并保证它不会大于初始值,如果超过就会引发一个ValueError错误,常用于守护限制访问资源。
使用Event对象
在Python中,事件对象是线程间最简单的通信机制之一,线程可以激活在一个事件对象上等待的其他线程。Event对象的实现类是threading.Event,
这是一个实现事件对象的类,一个event管理一个标志,该标志可以通过set()方法设置为真或通过clear()方法重新设置为假,wait()方法阻塞,直到标志为真,该标志
初始值为假。
import threading, time
event = threading.Event()
def func():
#
print("%s wait for event ..."%threading.currentThread().getName())
# event.wait(timeout) 阻塞线程,直到event对象内部标实位为True,或超时
event.wait()
print("%s recv event."%threading.currentThread().getName())
t1 = threading.Thread(target=func)
t2 = threading.Thread(target=func)
t1.start()
t2.start()
time.sleep(2)
print("MainTread set event")
event.set() # 将标识位改为True
"""
Thread-1 wait for event ...
Thread-2 wait for event ...
MainTread set event
Thread-2 recv event.
Thread-1 recv event."""
使用Timer对象
Timer:定时器,用于在指定时间后调用一个方法,相应的可以通过cancel() 函数取消相应的timer动作。
import threading
def func():
print("ping")
timer = threading.Timer(5,func)
timer.start()

浙公网安备 33010602011771号