Python通过两个标准库_thread 和threading提供对线程的支持 , threading对_thread进行了封装。threading模块中提供了Thread , Lock , RLock , Condition等组件。
因此在实际的使用中我们一般都是使用threading
1 线程的实现:
在python中有两种方法实现创建线程,实例Thread类和继承重写Thread类
实例Thread类
from threading import Thread,Lock
import time
def a(name):
print('好好学习%s'%name)
time.sleep(3)
print('天天向上')
def b(name):
print('你好%s'%name)
time.sleep(3)
print('世界')
# a()
# b()
#创建子线程
a_thread= Thread(target=a,args=('王磊',),name='wl')
b_thread= Thread(target=b,args=('彤颖',),name='ty') #args是一个元祖,必须加逗号 args参数传给target调用对象 name是子线程的名称
#获取子线程的名称
print(a_thread.getName())
print(b_thread.getName())
#给子线程添加守护线程(主线程执行完,子线程也就完了)
a_thread.setDaemon(True)
b_thread.setDaemon(True)
#开启子线程
a_thread.start() #start方法启动run方法,run方法又调用子线程target参数(函数)
b_thread.start()
#子线程阻塞(等到子线程执行完才执行主线程)
a_thread.join()
b_thread.join()
print('主线程执行完了')
继承Thread类
join和setDaemon区别:
在说这两个方法之前,我们需要了解主线程和子线程的概念
主线程 : 当一个程序启动时 , 就有一个线程开始运行 , 该线程通常叫做程序的主线程
子线程 : 因为程序是开始时就执行的 , 如果你需要再创建线程 , 那么创建的线程就是这个主线程的子线程
主线程的重要性体现在两方面 :
1. 是产生其他子线程的线程
2. 通常它必须最后完成执行比如执行各种关闭操作
join:阻塞调用程序,直到调用join方法的线程执行完,才继续往下执行
setDaemon:join会等子线程执行完才往下执行,setDaemon不会等,主线程执行完,子线程跟着结束
线程间的通信:
互斥锁:在多线程中 , 所有变量对于所有线程都是共享的 , 因此 , 线程之间共享数据最大的危险在于多个线程同时修改一个变量 , 那就乱套了 , 所以我们需要互斥锁 , 来锁住数据。
from threading import Thread,Lock
lock= Lock() #线程锁
x= 0
n= 10000000
def a(n):
global x
for i in range(n):
lock.acquire() #加锁
x+= 1
lock.release() #解锁
def b(n):
global x
for i in range(n):
lock.acquire()
x-= 1
lock.release()
#创建子线程
a_thread= Thread(target=a,args=(n,))
b_thread= Thread(target=b,args=(n,))
a_thread.start() #开启线程
b_thread.start()
a_thread.join() #要等子线程执行完才能往下执行
b_thread.join()
print(x)
#队列:先进先出
入队:put() 出对:get() 测试空:empty() 测试满: full() 队列长度:qsize() 任务结束:task_done() 等待完成:join()
#队列
from queue import Queue
q= Queue(5)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #队列是否空
print(q.full()) #队列是否满
print(q.qsize()) #队列中的剩余长度
q.task_done()
q.task_done()
q.task_done()
q.task_done() #没get取出一次,就结束一次
q.task_done()
q.join() #队列执行完了才能往下执行,怎么看执没执行完,看task_done调用次数,
print('主线程执行完了')
线程池:
主线程: 相当于生产者,只管向线程池提交任务。 并不关心线程池是如何执行任务的。因此,并不关心是哪一个线程执行的这个任务。
线程池: 相当于消费者,负责接收任务,并将任务分配到一个空闲的线程中去执行。
##自己手写实现线程池:
from threading import Thread
from queue import Queue
import time
class MyThreadPool:
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,func,args=(),kwds={}):
self.queue.put((func,args,kwds))
def join(self):
self.queue.join()
def task(name):
time.sleep(2)
print('%s好好学习'%name)
pool= MyThreadPool(5) #创建实例 5个子线程
for i in range(10): #一个函数执行10次,就是10个任务
pool.apply_async(task,(i,))
pool.join() #线程阻塞,等待子线程执行完了,再能往下执行
print('主线程执行完了')
##python内置线程池
from multiprocessing.pool import ThreadPool
import time
def task1(name):
time.sleep(2)
print('%s好好学习'%name)
def task2(*args,**kwargs):
time.sleep(2)
print('天天向上',args,kwargs)
def task3():
time.sleep(2)
print('你好')
def task4():
time.sleep(2)
print('世界')
pool= ThreadPool(4) #线程池设有四个子线程
pool.apply_async(task1,('王磊',))
pool.apply_async(task2,(1,2,3),{'a':1,'b':2})
pool.apply_async(task3)
pool.apply_async(task4)
pool.close() #线程池关闭 就不能再提交任务了
pool.join() #线程池中的子线程默认开启,并且是守护线程,所以要阻塞(子线程执行完了才能往下执行)
print('主线程执行完了')