python多线程
一,线程的实现
1.进程,线程的概念
程序
- 驱动程序 - 物理硬件
- 操作系统- 组织硬件,管理,运行应程序
- 应用程序-qq,微信,吃鸡
应用程序是运行在操作系统上的。
-
进程
操作系统的概念,存储在硬盘上的可执行的二进制数据(可执行文件),在被操作系统运行之后进行资源分配和调度的基本单位。
-
线程
线程是操作系统能够进行运算调度的最小单元,它包含在进程中,是进程的实际运作单位,一个线程指的是进程中一个单一顺序的控制流,一个进程可以并发多个线程。
任何进程都会默认启动一个线程,我们把该线程成为主线程。
### 2.python操作线程
_thread, threading
-
创建线程
import threading def task(): """子线程要执行的任务""" current_thread = threading.current_thread() print('我是线程 %s 我在执行任务' % current_thread.name) if __name__ == '__main__': # 创建一个新的线程 t = threading.Thread(name='abc', target=task) # 启动线程 t.start() current_thread = threading.current_thread() print('我是线程 %s' % current_thread.name)
-
利用多线程来分担耗时任务
import threading import time def task(n): """模拟线程执行的耗时任务""" t = threading.current_thread() print('线程 %s 开始处理任务' % t.name) time.sleep(n) if __name__ == '__main__': t1 = threading.Thread(target=task, args=(3,)) t2 = threading.Thread(target=task, args=(3,)) t3 = threading.Thread(target=task, args=(3,)) t1.start() t2.start() t3.start() print('主线程执行完毕!')
-
线程同步
默认,主线程不会等待子线程的任务结束后再结束。
通过执行子线程的join方法,阻塞主线程,让其等待子线程结束。
import threading import time def task(n): """模拟线程执行的耗时任务""" t = threading.current_thread() print('线程 %s 开始处理任务' % t.name) time.sleep(n) if not threading.main_thread().isAlive(): print('领导已经走了!') if __name__ == '__main__': s_time = time.time() t1 = threading.Thread(target=task, args=(1,)) t2 = threading.Thread(target=task, args=(2,)) t3 = threading.Thread(target=task, args=(3,)) t1.start() t2.start() t3.start() t3.join() # 阻塞 主线程 # t2.join() # t3.join() e_time = time.time() print('任务总耗时 %s 秒' % (e_time-s_time)) print('主线程执行完毕!')
-
守护线程
通过设置子线程的daemon=True,将它设置为守护线程,当主线程结束后,即使任务没有完成,它也会被结束。
import threading import time def task(n): """模拟线程执行的耗时任务""" t = threading.current_thread() print('线程 %s 开始处理任务' % t.name) time.sleep(n) if not threading.main_thread().isAlive(): print('领导已经走了!') def task2(): t = threading.current_thread() while True: print('我是线程 %s ' % t.name) time.sleep(1) if __name__ == '__main__': s_time = time.time() t1 = threading.Thread(target=task, args=(1,)) t2 = threading.Thread(target=task, args=(2,)) t3 = threading.Thread(target=task2, daemon=True) t1.start() t2.start() t3.start() t1.join() # 阻塞 主线程 t2.join() # t3.join() e_time = time.time() print('任务总耗时 %s 秒' % (e_time-s_time)) print('主线程执行完毕!')
二, 线程间的资源共享
1. 线程间全局变量共享
import threading
import time
A = 1
def fun():
print('我是子线程,我要修改全局变量A的值')
global A
A = 2
if __name__ == '__main__':
print('我是主线程,现在A的值是:%s' % A)
t = threading.Thread(target=fun)
t.start()
t.join()
print('我是主线程,现在A的值是:%s' % A)
2. 线程间资源竞争问题
import threading
import time
A = 0
def task_add():
global A
for i in range(1000000):
A += 1
def task_red():
global A
for i in range(1000000):
A -= 1
if __name__ == '__main__':
t1 = threading.Thread(target=task_add)
t2 = threading.Thread(target=task_red)
t1.start()
t2.start()
t1.join()
t2.join()
print(A)
高级语言的一条语句,cpu去执行的时候是若干条,即便一个简单的计算:
A += 1
-
将A + 1, 存入临时变量
-
将临时变量,赋值给A
x = A + 1 A = x 初始值 A = 0 t1: x1 = A + 1 x1 = 1 t1: A = x1 A = 1 t2: x2 = A - 1 x2 = 0 t2: A = x2 A = 0 初始值:A = 0 t1: x1 = A + 1 x1 = 1 t2: x2 = A - 1 x2 = -1 t1: A = x1 A = 1 t2: A = x2 A = -1
3. 互斥锁
Thread对象提供了一种锁的机制,一个线程在操作一个变量之前需要先获取一把锁 ,然后再操作变量,操作结束后,再释放这把锁。再释放之前,其他的线程都不能操作。
import threading import time A = 0 def task_add(lock): lock.acquire() # 获取锁, 如果锁被其他线程获取了,这时,就会阻塞 global A #声明A为全局变量 for i in range(1000000): A += 1 lock.release() # 操作完,需要释放 def task_red(lock): lock.acquire() # 获取锁,如果锁被其他线程获取了,这时,就会阻塞 # 。。。 global A for i in range(1000000): A -= 1 lock.release() # 操作完,你要释放 if __name__ == '__main__': # 创建一把锁 lock = threading.Lock() t1 = threading.Thread(target=task_add, args=(lock, )) t2 = threading.Thread(target=task_red, args=(lock, )) t1.start() t2.start() t1.join() t2.join() print(A)
4. 队列
python的queue模块提供了同步的,线程安全的,实现了锁原语的队列类,包括FIFO,LIFO,优先级队列。
from queue import Queue q = Queue(maxsize=10) # maxsize 设置队列的大小 q.put(1) # 往队列中放数据,可以放入任何对象 q.put((1, )) q.put('abc') print(q.qsize()) # 输出队列长度 item = q.get() # 取出一个数据 q.task_done() # 给q一个信号,任务执行完毕 print(item) q.get() q.get() q.task_done() q.task_done() q.join() # 阻塞,等待队列任务执行完毕 print('111111111111')
5. 生产者消费者模型
生产者消费者模型实现了:
- 生产者与消费者的解耦
- 平衡了生产力与消费力
三,线程池
为了更好地利用多线程,出现了池的概念。所有的子线程放到一个池中,由池对子线程进行调度和管理,提高系统资源的利用率。
1.线程池的简单实现
import os
import time
import random
import threading
from queue import Queue
class MyPool:
def __init__(self, size):
if size is None:
size = os.cpu_count() or 1
if size < 1:
raise ValueError('Number of thread must be at lease 1')
self.size = size # 线程数量
self.queue = Queue() # 任务队列
# 创建线程
for i in range(self.size):
t = threading.Thread(target=self.job, daemon=True)
t.start()
def job(self):
# 线程任务
while True:
# (func, args, kwargs)
# 没有任务堵在这里
func, args, kwargs = self.queue.get() # 获取任务
func(*args, **kwargs) # 执行任务
self.queue.task_done() # 执行完毕,通知队列
# 添加任务
def apply_async(self, func, args=(), kwargs=None):
if kwargs is None:
kwargs = {}
self.queue.put((func, args, kwargs))
# 阻塞主线程,知道任务完成
def join(self):
self.queue.join()
def task1():
print('我是线程 %s 我开始执行task1' % threading.current_thread().name)
time.sleep(random.randint(1, 3))
print('我是线程 %s 我的任务执行完毕' % threading.current_thread().name)
def task2(*args, **kwargs):
print('我是线程 %s 我开始执行task2' % threading.current_thread().name)
print('我接收到的参数是:', args, '|', kwargs)
time.sleep(random.randint(1, 3))
print('我时线程 %s 我的任务执行完毕' % threading.current_thread().name)
if __name__ == '__main__':
pool = MyPool(5)
for i in range(10):
if i % 2 == 0:
pool.apply_async(task1)
else:
pool.apply_async(task2, args=(1, 2), kwargs={'a': 1, 'b': 3})
print('任务提交完成')
pool.join()
print('任务执行完毕')