Python多线程(二): 线程同步 生产者消费者模式 ThreadLocal线程局部变量
线程同步
同步就是协同步调,按预定的先后次序进行运行。例如:开会。“同”字指协同、协助、互相配合。
如进程、线程同步,可以理解为进程或线程 A 和 B 一块配合, A 执行到一定程度时要依靠 B 的某个结果,于是停下来,示意 B 运行, B 运行后将结果给 A, A 继续运行
线程同步应用
import time from threading import Thread,Lock import threading lock1=Lock() lock2=Lock() lock3=Lock() lock2.acquire() lock3.acquire() class Task1(Thread): def run(self): while True: if lock1.acquire(): print('...task1...') time.sleep(1) lock2.release() class Task2(Thread): def run(self): while True: if lock2.acquire(): print('...task2...') time.sleep(1) lock3.release() class Task3(Thread): def run(self): while True: if lock3.acquire(): print('...task3...') time.sleep(1) lock1.release() if __name__ == '__main__': t1=Task1() t2=Task2() t3=Task3() t1.start() t2.start() t3.start()
生产者-消费者模式
生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入生产者和消费者模式
生产者消费者模式通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者之间不直接通信。 生产者生产商品, 然后将其放到类似队列的数据结构中, 消费者不找生产者要数据,而是直接从队列中取。 这里使用 queue 模块来提供线程间通信的机制,也就是说,生产者和消费者共享一个队列。生产者生产商品后,会将商品添加到队列中。消费者消费商品,会从队列中取出商品
import time import threading from queue import Queue class Producer(threading.Thread): def run(self): global queue count=0 while True: if queue.qsize()<1000: for i in range(100): count += 1 msg = '生成产品' + str(count) queue.put(msg) print(msg) time.sleep(0.5) class Consumer(threading.Thread): def run(self): global queue while True: if queue.qsize()>100: for i in range(3): msg=self.name+'消费了'+queue.get() print(msg) time.sleep(1) if __name__ == '__main__': queue = Queue() p=Producer() p.start() time.sleep(1) c=Consumer() c.start()
ThreadLocal线程局部变量
我们知道多线程环境下,每一个线程均可以使用所属进程的全局变量。如果一个线程对全局变量进行了修改,将会影响到其他所有的线程对全局变量的计算操作,从而出现数据混乱,即为脏数据。为了避免多个线程同时对变量进行修改,引入了线程同步机制,通过互斥锁来控制对全局变量的访问。所以有时候线程使用局部变量比全局变量好,因为局部变量只有线程自身可以访问,同一个进程下的其他线程不可访问
从上面的实例可以看到每个函数一层一层调用都需要传递 std 参数,非常麻烦,如果使用全局变量也不行,因为每个线程处理不同的 Student 对象,不能共享。 因此 Python 还提供了ThreadLocal 变量,它本身是一个全局变量,但是每个线程却可以利用它来保存属于自己的私有数据,这些私有数据对其他线程也是不可见的
import threading # 创建全局 ThreadLocal 对象: local = threading.local() def process_student(): # 获取当前线程关联的 name: student_name = local.name print('线程名: %s 学生姓名:%s' % (threading.current_thread().name,student_name)) def process_thread(name): # 绑定 ThreadLocal 的 name: local.name = name process_student() t1 = threading.Thread(target=process_thread, args=('张三',), name='Thread-A') t2 = threading.Thread(target=process_thread, args=('李四',), name='Thread-B') t1.start() t2.start() t1.join() t2.join()