引入一下线程和进程的理解性概念:
进程: 打开一个程序至少会有一个进程 它是cpu调度的最小的单位。
线程: 程序执行的最小单位,一个进程里面至少有一个线程,cpu会控制进程里面的线程。
打个比方:(1)打开一个qq就是一个进程的话,那么你可以同时和好多人聊天,和一个人聊天这就是一个线程。
(2)再打个比方,一条直行的高速公路,分好几个车道,这整个告诉公路就相当于一个进程,
那些车道就相当于一个个线程,如果有一条车道上的车拐弯,别的车道的车就要等待,不然就撞车了。
注意:
(1)一个cpu同一时间只能处理一件事,如果同时有多个任务,那么就轮换着执行,但是每个任务执行的时间非常短暂,无法感受到。
(2)使用线程的时候,不管它的顺序,因为cpu是随机调度的。
(3)一个程序的执行,就会有一个主线程
阶段一:线程的实现
1.线程模块
Python通过两个标准库thread 和threading提供对线程的支持 , threading对thread进行了封装。threading模块中提供了Thread , Lock , RLock , Condition等组件。
因此在实际的使用中我们一般都是使用threading
2.Thread类
3.创建线程
在python中创建线程有两种方式,实例Thread类和继承重写Thread类实例Thread类
实例Thread类:
4.创建线程
继承Thread类
5.Join & setDaemon
(1)
在说这两个方法之前 , 需要知道主线程与子线程的概念
主线程 : 当一个程序启动时 , 就有一个线程开始运行 , 该线程通常叫做程序的主线程
子线程 : 因为程序是开始时就执行的 , 如果你需要再创建线程 , 那么创建的线程就是这个主线程的子线程
主线程的重要性体现在两方面 : 1. 是产生其他子线程的线程 2. 通常它必须最后完成执行比如执行各种关闭操作
(2)
join : 阻塞调用程序 , 直到调用join () 方法的线程执行结束, 才会继续往下执行
阶段二:线程通信
1.互斥锁
在多线程中 , 所有变量对于所有线程都是共享的 , 因此 , 线程之间共享数据最大的危险在于多个线程同时修改一个变量 , 那就乱套了 , 所以我们需要互斥锁 , 来锁住数据。
2.线程间全局变量的共享
提示!因为线程属于同一个进程,因此它们之间共享内存区域。因此全局变量是公共的。
3.共享内存间存在竞争问题
from threading import Thread
x = 0
n =1000000
def a(n):
global x
for i in range(n):
x += 1
def b(n):
global x
for i in range(n):
x -= 1
if __name__ == '__main__':
a = Thread(target=a,args = (n,))
b = Thread(target=b,args = (n,))
a.start()
b.start()
a.join()
b.join()
print(x)
提示! 如果1000000不能出现效果可以继续在后面加0
你会发现这个结果千奇百怪!!!
4.使用锁来控制共享资源的访问
下面引入互斥锁
在多线程中 , 所有变量对于所有线程都是共享的 ,因此 ,线程之间共享数据最大的危险在于多个线程同时修改一个变量 , 那就乱套了 , 所以我们需要互斥锁 , 来锁住数据。
只要我们操作全局变量的时候,就在操作之前加锁,再操作完之后解锁,就解决了这个资源竞争的问题!!!
5.队列的基本概念
一个入口,一个出口先入先出(FIFO)
队列操作一览:
入队: put(item)
出队: get()
测试空: empty()
测试满: full()
队列长度: qsize()
任务结束: task_done()
等待完成: join()
注意:
get()等待任务完成,如果不加task_done()则不表示任务完成,只要加这句才表明完成。才会结束执行。
join就是阻塞,直到这个任务完成(完成的标准就是每次取出都task_done()了)
阶段三:线程池
1.池的概念
主线程: 相当于生产者,只管向线程池提交任务。
并不关心线程池是如何执行任务的。
线程池: 相当于消费者,负责接收任务,
并将任务分配到一个空闲的线程中去执行。
因此,并不关心是哪一个线程执行的这个任务。
2.线程池的简单实现
from threading import Thread
from queue import Queue
import time
class ThreadPool:
def __init__(self,n):
self.queue = Queue()
for i in range(n):
Thread(target = self.worker,daemon = True).start()
def worker(self):
while True:
func,args,kwargs = self.queue.get()
func(*args,*kwargs)
self.queue.task_done()
def apply_async(self,target,args = (),kwargs = {}):
self.queue.put((target,args,kwargs))
def join(self):
self.queue.join()
def fun(x):
print('hello 第%s次'%x)
time.sleep(3)
print('帅哥美女就给点赞啦!')
t = ThreadPool(2)
for i in range(10):
t.apply_async(fun,args = (i,))
t.join()
3.python内置线程池
from multiprocessing.pool import ThreadPool
import time
pool = ThreadPool(2) #创建两个线程
def funa(x,y):
print('%s好好学习'%x)
time.sleep(3)
print('天天向上')
def funb(x,y):
print('%shello'%x)
time.sleep(3)
print('world')
#我们这就是有一个线程池,里面有两个等待处理任务的线程,然后这两个函数就是两个任务,
#线程池里一个线程处理一个,所以会同时输出!如果多于两个任务就会执行等待sleep
pool.apply_async(funa,args = ('我们要————',2)) #将任务添加到线程池
pool.apply_async(funb,args = ('大家要————',4))
pool.close() #close之后则无法向线程池提交任务
#内置线程池,自带守护线程,主线程结束,子线程也跟着结束
#所以需要加阻塞,否则主线程一结束,子线程也跟着结束,无输出
pool.join() #在join之前可使用终止线程,直接终止线程pool: pool.terminate()
print('这是程序的最后一行,执行到这里,主线程结束')
4.池的其他操作
操作一: close - 关闭提交通道,不允许再提交任务
操作二: terminate - 中止进程池,中止所有任务