1、普通同步
用threading.Lock()创建锁,用acquire()申请锁,每次只有一个线程获得锁,其他线程必须等此线程release()后才能获得锁
RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即同一线程中调用了n次acquire,必须调用n次的release才能真正释放所占用的琐
2、条件同步
Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法Condition([Lock/RLock]),否则它将自己生成一个RLock实例。
可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程通过wait()处于状态图中的等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定
acquire([timeout])/release(): 调用申请/释放锁。
wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。
notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
生产者消费者问题:
3、同步队列
Python中的Queue对象也提供了对线程同步的支持。使用Queue对象可以实现多个生产者和多个消费者形成的FIFO的队列。
生产者将数据依次存入队列,消费者依次从队列中取出数据
Queue的默认长度是无限的,但是可以设置其构造函数的maxsize参数来设定其长度。Queue的put方法在队尾插入。
Queue的get方法是从队首取数据,如果block参数为true且timeout为None(缺省值),线程被block,直到队列中有数据。如果timeout大于0,在timeout时间内,仍然没有可取数据,Empty exception被抛出。
反之,如果block参数为false(忽略timeout参数),队列中的数据被立即取出。如果此时没有可取数据,Empty exception也会被抛出。
Producer在随机的时间内生产一个“产品”,放入队列中。Consumer发现队列中有了“产品”,就去消费它。
本例中,由于Producer生产的速度快于Consumer消费的速度,所以往往Producer生产好几个“产品”后,Consumer才消费一个产品。