Python threading多线程模块的用法(附带实例)
多个进程可以实现多个任务,一个进程内的多个线程(多线程)同样可以实现多个任务。
进程是由若干个线程组成的。一个进程中至少要有一个线程。同一个进程内的多个线程资源共享。线程之间的切换开销比多个进程的切换开销更低。
threading 模块提供的方法有:
首先创建一个 Thread 实例并传入函数,然后调用 start() 即可启动一个新的线程,代码为:
现在假设有一个水池,现有的水量为 1000。有两个线程:
各自重复执行 100000 次,查看水池里的水量。测试代码为:
water 值的计算从 CPU 的角度需要多个步骤,每个步骤的线程都可能被中断,则多个线程就会把同一个对象的内容改乱。为了避免这样的混乱产生,threading 模块提供了多种机制实现线程间的同步。其中,Lock(锁)就是常用的方法之一。
当一个线程修改 water 值时,通过加锁操作可防止其他线程同时进行修改操作,从而避免了 water 值被改错,代码为:
除了 Lock,threading 模块还提供了 RLock、Semaphore 等多种同步机制。
接下来以生产者和消费者为模型,讲解 threading 模块 Queue 的使用。
Queue 用于建立和操作队列,常与 threading 模块一起建立一个简单的线程队列。队列有很多种,根据进出顺序可以分为:
其中,FIFO 是最常用的队列,常用的方法有:
接下来编写一个程序代码:一个线程(生产者)生成数据并将数据加入队列;另一个线程(消费者)去队列中提取这些数据,程序代码保存在文件 queue_thread.py 中,即:
进程是由若干个线程组成的。一个进程中至少要有一个线程。同一个进程内的多个线程资源共享。线程之间的切换开销比多个进程的切换开销更低。
Python threading模块
Python 通过 threading 模块提供了对多线程的支持,使用 threading 模块创建线程的方法有:- 创建一个 Thread 实例,传给它一个函数;
- 创建一个 Thread 实例,传给它一个可调用的类对象;
- 由 Thread 派生一个子类,创建这个子类的实例。
threading 模块提供的方法有:
- start():开始线程的执行;
- run():定义线程的功能函数(一般会被子类重写);
- join(timeout=None):程序挂起,直到线程结束,如果给了 timeout,则最多阻塞 timeout 秒;
- getName():返回线程的名字;
- setName(name):设置线程的名字;
- isAlive():布尔标志,表示这个线程是否还在运行中;
- isDaemon():返回线程的 daemon 标识;
- setDaemon(daemonic):把线程的 daemon 标识设置为 daemonic。
首先创建一个 Thread 实例并传入函数,然后调用 start() 即可启动一个新的线程,代码为:
import time import threading def thread1(): while True: print('thread1 is running') time.sleep(2) if __name__ == '__main__': t1 = threading.Thread(target=thread1, name="") t1.start() while True: print('Main Thread is running') time.sleep(2)任何进程都会默认启动一个线程,也就是主线程。执行程序代码后,可以看到主线程和子线程一直处于运行状态,即:
thread1 is running
Main Thread is running
Main Thread is running
thread1 is running
Main Thread is running
thread1 is running
Main Thread is running
Python threading线程同步
主线程和子线程之间相互独立,两者之间没有任何关系。现在假设有一个水池,现有的水量为 1000。有两个线程:
- 一个线程负责往水池里面加水,每次加 1;
- 另一个线程负责从水池里往外抽水,每次抽出的水量也为 1。
各自重复执行 100000 次,查看水池里的水量。测试代码为:
import threading import time water = 1000 def add_water(): global water for i in range(100000): water += 1 def sub_water(): global water for i in range(100000): water -= 1 if __name__ == '__main__': t_add = threading.Thread(target=add_water, name="") t_sub = threading.Thread(target=sub_water, name="") t_add.start() t_sub.start() t_add.join() t_sub.join() print(water)多次执行以上程序后,得到的结果为:
1000
-24335
91567
- 计算 water+1,并将计算结果存入临时变量,如 a;
- 将临时变量,也就是 a 赋值给 water。
water 值的计算从 CPU 的角度需要多个步骤,每个步骤的线程都可能被中断,则多个线程就会把同一个对象的内容改乱。为了避免这样的混乱产生,threading 模块提供了多种机制实现线程间的同步。其中,Lock(锁)就是常用的方法之一。
当一个线程修改 water 值时,通过加锁操作可防止其他线程同时进行修改操作,从而避免了 water 值被改错,代码为:
import threading import time water = 1000 lock = threading.Lock() def add_water(): global water lock.acquire() for i in range(100000): water += 1 lock.release() def sub_water(): with lock: global water for i in range(100000): water -= 1 if __name__ == '__main__': t_add = threading.Thread(target=add_water, name="") t_sub = threading.Thread(target=sub_water, name="") t_add.start() t_sub.start() t_add.join() t_sub.join() print(water)以上是使用 Lock 之后的程序代码,多次执行后,水量总是 1000,不会被改乱。
除了 Lock,threading 模块还提供了 RLock、Semaphore 等多种同步机制。
Python threading线程间通信
与多进程一样,Python 的 threading 模块同样提供了队列(Queue)、管道(Pipe)等多线程间的通信方式。在 Python 中,队列是线程间最常用的交换数据形式。Queue 是线程安全的自带锁,使用时,不用对队列进行加锁操作。接下来以生产者和消费者为模型,讲解 threading 模块 Queue 的使用。
Queue 用于建立和操作队列,常与 threading 模块一起建立一个简单的线程队列。队列有很多种,根据进出顺序可以分为:
- Queue.Queue(maxsize):FIFO(先进先出队列);
- Queue.LifoQueue(maxsize):LIFO(先进后出队列);
- Queue.PriorityQueue(maxsize):优先度越低的越先出来。
其中,FIFO 是最常用的队列,常用的方法有:
- Queue.qsize():返回队列的大小;
- Queue.empty():如果队列为空,则返回值为 True,反之为 False;
- Queue.full():如果队列满了,则返回值为 True,反之为 False;
- Queue.get([block[,timeout]]):获取队列,timeout 为等待时间;
- Queue.get_nowait():相当 Queue.get(False) 非阻塞;
- Queue.put(item):写入队列,timeout 为等待时间;
- Queue.put_nowait(item):相当 Queue.put(item,False)。
接下来编写一个程序代码:一个线程(生产者)生成数据并将数据加入队列;另一个线程(消费者)去队列中提取这些数据,程序代码保存在文件 queue_thread.py 中,即:
import threading, time import queue q = queue.Queue() def Producer(): n = 0 while n < 5: n += 1 q.put(n) print('Producer has created %s' % n) time.sleep(0.1) def Consumer(): count = 0 while count < 5: count += 1 data = q.get() print('Consumer has used %s' % data) time.sleep(0.2) p = threading.Thread(target = Producer, name="") c = threading.Thread(target = Consumer, name="")运行结果为:
Producer has created 1
Consumer has used 1
Producer has created 2
Consumer has used 2
Producer has created 3
Producer has created 4
Consumer has used 3
Producer has created 5
Consumer has used 4
Consumer has used 5