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()

 

posted @ 2020-04-12 15:01  独角兕大王  阅读(497)  评论(0编辑  收藏  举报