一、Lock版锁机制

多线程都是在同一个进程中运行的。因此在进程中的全局变量所有线程都是可共享的。这就造成了一个问题,因为线程执行的顺序都是无序的,有可能会造成数据错误,代码如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import threading
VALUE = 0
def add_value():
    global  VALUE
    for i in range(10000):
        VALUE += 1
    print('VALUE值为:', VALUE)

def main():
    for x in range(2):
        t  = threading.Thread(target=add_value)
        t.start()

if __name__ == '__main__':
    main()

因此需要在修改全局变量的时候给它加个锁,改善后代码:
示例一:增加数字

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# acquire:上锁
# release:解锁

import threading
VALUE = 0
Lock = threading.Lock()

def add_value():
    global  VALUE
    Lock.acquire()
    for i in range(10000):
        VALUE += 1
    print('VALUE值为:', VALUE)
    Lock.release()

def main():
    for x in range(2):
        t  = threading.Thread(target=add_value)
        t.start()

if __name__ == '__main__':
    main()

示例二:生产者和消费者模型
模型:生产者生产 ===> 全局变量 ===> 消费者消费变量

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import threading,random,time
VALUE = 1000
Lock = threading.Lock()

class Product(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(100,500)
            Lock.acquire()
            VALUE += m
            print('%s 生成了数字 %s ,现在数字大小为 %s ' %(threading.current_thread(),m,VALUE))
            Lock.release()
            time.sleep(0.5)

class Consumer(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(200,600)
            Lock.acquire()
            if VALUE >= m:
                VALUE -= m
                print('%s 消费了数字 %s ,现在数字大小为 %s ' %(threading.current_thread(),m,VALUE))
            else:
                print('数字 %s 太小' %VALUE)
            Lock.release()
            time.sleep(0.5)


def main():
    for x in range(3):
        t = Consumer(name='消费者线程%d' %(x))
        t.start()

    for x in range(5):
        t = Product(name='生产者线程%d' %(x))
        t.start()

if __name__ == '__main__':
    main()
二、Condition版锁机制

由于上锁是一个很耗费CPU资源的行为,因此 Lock锁机制 方式不是很友好。还有一种是通过 threading.Condition 来实现。 threading.Condition 可以在没有数据的时候处于阻塞等待状态,一旦有了合适的数据,可以使用 notify 相关的函数来通知其他等待的线程。这样可以不用做一些无用的上锁和解锁的操作,提高程序的性能。,threading.Condition类似threading.Lock,可以在修改全局数据的时候进行上锁,也可以在修改完毕后进行解锁。

acquire:上锁。
release:解锁。
wait:将当前线程处于等待状态,并且会释放锁。可以被其他线程使用notify和notify_all函数唤醒。被唤醒后会继续等待上锁,上锁后继续执行下面的代码。
notify:通知某个正在等待的线程,默认是第1个等待的线程。
notify_all:通知所有正在等待的线程。notify和notify_all不会释放锁。并且需要在release之前调用

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# 生产者和消费者模型

import threading,random,time
VALUE = 1000
cLock = threading.Condition()

class Product(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(100,500)
            cLock.acquire()
            VALUE += m
            print('%s 生成了数字 %s ,现在数字大小为 %s ' %(threading.current_thread(),m,VALUE))
            cLock.notify_all()  # 通知wait()
            cLock.release()
            time.sleep(0.5)

class Consumer(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(200,600)
            cLock.acquire()
            if VALUE >= m:
                VALUE -= m
                print('%s 消费了数字 %s ,现在数字大小为 %s ' %(threading.current_thread(),m,VALUE))
            else:
                print('数字 %s 太小' %VALUE)
                cLock.wait()   # 全局值太小了,那就等待,直到通知时候再去排队
            cLock.release()
            time.sleep(0.5)


def main():
    for x in range(3):
        t = Consumer(name='消费者线程%d' %(x))
        t.start()

    for x in range(2):
        t = Product(name='生产者线程%d' %(x))
        t.start()

if __name__ == '__main__':
    main()