【python标准库学习】thread,threading(二)多线程同步

上一篇介绍了python的多线程和基本用法。也说到了python中多线程中的同步锁,这篇就来看看python中的多线程同步问题。

有时候很多个线程同时对一个资源进行修改,这个时候就容易发生错误,看看这个最简单的程序:

import thread, time

count = 0

def addCount():
    global count
    for i in range(100000):
        count += 1

for i in range(10):
    thread.start_new_thread(addCount, ())

time.sleep(3)
print count

创建了10个线程,每个线程对count每次增加1,共增加10万次,我们想象的结果应该是100万,但是事实真的如此吗,下面是我运行几次程序得到的结果:

每次的结果都不一样,而且都小于100万,怎么会酱紫?因为我们想象的是线程一个一个的进行count += 1的操作,就是每个线程操作count结束后另外一个线程再去操作它,可是事实却并非如此,加入刚开始的时候,线程1拿到的count是0,在修改完成前另外一个线程2也拿到了这个count,此时count为0,然后线程1修改完成将count变成1,然后线程2也操作了一次再次将count变成1,这里就相当于浪费了一次操作,而在大量的线程进行大量的这种操作的时候就会有更多的浪费,所以最后的结果就编程了这个样子。

那么要达到我们理想的状态,就只能每次让一个线程来操作count,当它操作完成后再让别的线程去操作它,那么别的线程拿到的count就是前面那个线程操作完成后的count。这个时候就需要同步锁了,多个线程只有一个同步锁,谁抢到就可以进行操作,操作完成后再释放锁让别的线程去抢。

1.thread模块

thread模块中有一个锁类就是LockType,通过thread.allocate_lock()函数来创建这个锁对象,这个锁有3个函数:

1).acquire()

获取锁,有一个布尔值的参数,设置为True则会一直阻塞到获取锁成功,而且这个阻塞是不会被打断的,设置为False的话,就会立即返回,即使没获取到锁。如果获取成功则返回True,如果锁已经被别的线程获取,就返回False。

2).release()

释放锁。

3).locked()

判断锁是否被占用。

将上面程序进行加锁操作:

import thread, time

count = 0
lock = thread.allocate_lock()

def addCount():
    global count, lock
    lock.acquire()
    for i in range(100000):
        count += 1
    lock.release()

for i in range(10):
    thread.start_new_thread(addCount, ())

time.sleep(3)
print count

2.threading模块

threading模块中有Lock,RLock和Condition来控制同步锁的问题,有的时候很多的线程都需要针对同一个事件作出反应,当这个事件为False的时候线程就阻塞,当事件为True的时候就执行,这个时候就需要Event了。

1).Lock和RLock

Lock和RLock的用法跟thread模块中的LockType一样,但是他们之间的区别是RLock可以被同一线程多次获取,但是Lock不可以,如果同一线程对Lock锁连续获取两次就会出现死锁的情况,因为它第二次会阻塞无法中断,而锁又无法释放,而RLock获取几次就必须要释放几次:

lock = threading.Lock() #Lock对象
lock.acquire()
lock.acquire()  #产生了死琐。
lock.release()
lock.release()
rLock = threading.RLock()  #RLock对象
rLock.acquire()
rLock.acquire() #在同一线程内,程序不会堵塞。
rLock.release()
rLock.release()

2).Condition

同步锁只能提供最基本的同步,加入只在发生某个事件才访问一个资源,这个时候就需要条件变量Condition了。Condition不仅提供了锁的acquire和release方法,还提供了wait,notify和notifyAll方法。当线程通过acquire获得锁后因为某些资源的却是需要等待就调用wait方法进入等待状态,同时释放锁持有的锁让别的线程能够获得并执行,当资源准备充足的时候就可以调用notify通知其他线程,处于等待状态的线程被唤醒重新去acquire锁,或者wait的线程超时,因为wait有个参数timeout。Condition内部维护着一个锁对象,可以通过参数指定为Lock或者RLock,默认为RLock,还维护着一个waiting池,进入等待状态的线程就会被这个waiting池锁记录,当有线程调用了notify后,Condition就会从这个waiting池中找一个线程唤醒,通知它去acquire锁进入就绪状态等待被cpu的调度,而notifyAll就会唤醒waiting池中所有的线程通知它们去acquire锁。

最经典的条件变量的问题就是生产者消费者了,当资源为0时,消费者就wait等待生产者生产资源,当资源达到一定量时生产者就wait等待消费者来消费:

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import threading, time

class Producer(threading.Thread):

    run(self):
        for i in range(3):  # 这里只循环了3次,下面消费者也只循环3次
            global x
            print ‘producer acquire lock‘
            con.acquire()
            print ‘producer get lock‘
            if x > 0:       # 当资源量大于零就等待消费者消费
                print ‘x > 0, producer wait‘
                con.wait()
                print ‘producer wake up‘
            else:
                for i in range(5):   # 一次生产5
                    x += 1
                    print ‘producing...‘ + str(x)
                    time.sleep(1)    # 每隔1秒生产1
                print ‘producer notify consumer‘
                con.notify()
            print ‘producer release lock‘
            con.release()
            time.sleep(1)   # 留更多的时间给消费者去acquire锁

class Consumer(threading.Thread):

    def run(self):
        for i in range(3):
            global x
            print ‘consumer acquire lock‘
            con.acquire()
            print ‘consumer get lock‘
            if x == 0:
                print ‘x = 0, consumer wait‘
                con.wait()
                print ‘consumer wake up‘
            else:
                for i in range(5):
                    x -= 1
                    print ‘consuming...‘ + str(x)
                    time.sleep(1)
                print ‘consumer notify producer‘
                con.notify()
            print ‘consumer release lock‘
            con.release()
            time.sleep(1)

con.threading.Condition()
x = 5

p = Producer()
c = Consumer()
p.start()
c.start()

输出结果:

3).Event

有的时候多线程都等待某个事件的发生,当事件发生的时候,所有的线程就会被激活而执行。Event内置一个初始值为False的标志,调用set()就设置为True,调用clear()就设置为False,wait()方法则使所有线程处于等待状态,知道Event被设置为True。可以通过isSet方法查询Event对象的内置对象的当前状态。有点像车辆过红绿灯,灯变绿的时候就通行,灯变红就等待:

#! /usr/bin/env python
# -*_ coding: utf-8 -*-

import threading
import random
import time

class VehicleThread(threading.Thread):

    def __init__(self, threadName, event):
        threading.Thread.__init__(self, name=threadName)
        self.threadEvent = event

    def run(self):
        time.sleep(random.randrange(1, 10))   # 睡了随即1到10秒,让车辆陆陆续续的到达
        print ‘%s arrived at %s‘ % (self.getName(), time.ctime(time.time()))

        self.threadEvent.wait()    # 调用wait,处于等待状态直到Event为True
        print ‘%s passed through intersection at %s‘ % (self.getName(), time.ctime(time.time()))

greenLight = threading.Event()
vehicleThreads = []    # 存放所有的车辆线程

for i in range(1, 11):
    vehicleThreads.append(VehicleThread(‘Vehicle‘ + str(i), greenLight))

for vehicle in vehicleThreads:
    vehicle.start()

while threading.activeCount() > 1:    # 因为activeCount得到所有线程的数量,包括了主线程main,所以是>1
    greenLight.clear()    # 将Event标示为False,标示红灯,所有线程等待
    print ‘RED LIGHT! at:‘, time.ctime(time.time())
    time.sleep(3)

    print ‘GREEN LIGHT! at:‘, time.ctime(time.time())
    greenLight.set()    # 睡了3秒,红灯结束,将Event标示为True,所有线程被激活
    time.sleep(1)

输出结果:

【python标准库学习】thread,threading(二)多线程同步,布布扣,bubuko.com

时间: 2024-10-05 04:19:24

【python标准库学习】thread,threading(二)多线程同步的相关文章

【python标准库学习】thread,threading(一)多线程的介绍和使用

在单个程序中我们经常用多线程来处理不同的工作,尤其是有的工作需要等,那么我们会新建一个线程去等然后执行某些操作,当做完事后线程退出被回收.当一个程序运行时,就会有一个进程被系统所创建,同时也会有一个线程运行,这个线程就是主线程main,在主线程中所创建的新的线程都是子线程,子线程通常都是做一些辅助的事.python中提供了thread和threading两个模块来支持多线程. python中使用线程有两种方式,第一种是用thread模块的start_new_thread函数,另一种是用threa

【python标准库学习】re模块

1.什么是re 正则表达式一门相对通用的语言,在python中也有对正则表达式的支持,那就是的内置re模块.正则表达式就是一系列的规则去匹配字符串然后进行相应的操作,这些规则网上一搜一大片,而re则是运用正则表达式来提供一系列的功能强大的接口让我们来调用.通常我们在对日志文件进行操作的时候会对正则表达式运用的比较多来得到我们希望得到的数据. 2.python中的转义符 正则表达式中通常用反斜杠'\'来代表转义,'\d'代表数字等,但是python本身也是通过反斜杠'\'来表示转义,所以就和正则表

python标准库学习-random

想想这么多年,也是没有好好梳理一下自己的知识体系,以至于总是会有书到用时方恨少的遗憾. 最近既然有学习的动力,干脆就趁着这份工作不是特别忙的机会,写一点东西吧,也理理自己的逻辑思维能力. python有哪些库? 这个问题呢可以参照http://blog.csdn.net/python_wangjunji/article/details/8689297这篇博文来看. 当然咯,首先要先推荐一个可厉害的学习程序:Dash.学编程必备查询库,各种语言,专治"我要看源码病". 那第一篇呢,我就先

python标准库介绍——31 threading 模块详解

threading 模块 (可选) ``threading`` 模块为线程提供了一个高级接口, 如 [Example 3-1 #eg-3-1] 所示. 它源自 Java 的线程实现. 和低级的 ``thread`` 模块相同, 只有你在编译解释器时打开了线程支持才可以使用它 . 你只需要继承 //Thread// 类, 定义好 ``run`` 方法, 就可以创建一 个新的线程. 使用时首先创建该类的一个或多个实例, 然后调用 ``start`` 方法. 这样每个实例的 ``run`` 方法都会运

pythonasyncore异步IO由python标准库学习

# -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' import asynchat,asyncore,logging,socket #asyncore异步IO #作用:异常IO处理 #功能是处理IO对象,如套接字,从而能异步管理这些对象(而不是多个线程或者进程),包括类有dispatcher,这是一个套接字的包装器,提供了一些HOOK(钩子),从主循环函数loo()调用时可以处理连接以及读写事件 #服务器 #第一个类

[学习笔记] Python标准库的学习准备 [转]

Python标准库是Python强大的动力所在,我们已经在前文中有所介绍.由于标准库所涉及的应用很广,所以需要学习一定的背景知识. 硬件原理 这一部份需要了解内存,CPU,磁盘存储以及IO的功能和性能,了解计算机工作的流程,了解指令的概念.这些内容基础而重要. Python标准库的一部份是为了提高系统的性能(比如mmap),所以有必要了解基本的计算机各个组成部分的性能. 操作系统 在了解操作系统时,下面是重点: 1) 操作系统的进程管理,比如什么是UID, PID, daemon 2) 进程之间

Python标准库的学习准备

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Python标准库是Python强大的动力所在,我们已经在前文中有所介绍.由于标准库所涉及的应用很广,所以需要学习一定的背景知识. 硬件原理 这一部份需要了解内存,CPU,磁盘存储以及IO的功能和性能,了解计算机工作的流程,了解指令的概念.这些内容基础而重要. Python标准库的一部份是为了提高系统的性能(比如mmap),所以有必要了解基本的计算机各个组成部分的性能. 操作系统

Python标准库08 多线程与同步 (threading包)

Python主要通过标准库中的threading包来实现多线程.在当今网络时代,每个服务器都会接收到大量的请求.服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率.Python是一种网络服务器的后台工作语言 (比如豆瓣网),所以多线程也就很自然被Python语言支持. (关于多线程的原理和C实现方法,请参考我之前写的Linux多线程与同步,要了解race condition, mutex和condition variable的概念) 多线程售票以及同步 我们使用Python来实

Python 标准库一览(Python进阶学习)

转自:http://blog.csdn.net/jurbo/article/details/52334345 写这个的起因是,还是因为在做Python challenge的时候,有的时候想解决问题,连应该用哪个类库都不知道,还要去百度(我不信就我一个人那么尴尬TvT) 好像自从学习了基础的Python 语法,看了几本Python经典的书,知道了一些常见的类库.在几本语法应用熟练的情况下,如果不做题,像是无法显著的提高自己的知识储备了(所以叫你去做python challenge啊,什么都不会~~