线程
- 线程就是一堆指令,是CPU调度的最小单位
- 每个程序的内存都是独立的
- 线程的存在就是让程序并发运行
- 一个线程可以控制和操作其他线程
线程的格式
import _thread 多线程包
import win32api
def show(i):
win32api.MessageBox(0, "我是内容", "我是标题", 1)
# 元组用于传递参数
_thread.start_new_thread(show, (i,))
主线程与子线程
- 主线程死了,子线程也会死
- 后台线程可以用while True进行主线程阻塞
全局解释器锁GIL(Global Interpreter Lock)
- 某一时间点只有一个线程在执行、CPython特性
线程冲突(线程安全问题)
import _thread
num=0
def add():
global num
for i in range(1000000):
num+=1
print(num)
for i in range(5):
_thread.start_new_thread(add,())
while True:
pass
线程冲突产生原因
- 多个线程存在
- 多个线程操作同一资源
基于类实现多线程
import threading
import time
import win32api
class Mythread(threading.Thread):
def run(self):
win32api.MessageBox(0, "我是内容", "我是标题", 1)
for i in range(5):
t=Mythread() # 初始化
t.start() # 开启
join方法
import threading
import time
import win32api
class Mythread(threading.Thread):
def run(self):
win32api.MessageBox(0, "我是内容", "我是标题", 1)
myThreadList=[]
for i in range(5):
t=Mythread() # 初始化
t.start() # 开启
myThreadList.append(t)
for thd in myThreadList:
thd.join()
join方法介绍
- 让主线程等待该线程,相当于阻塞主线程
同步锁机制
mutex.acquire(1) # 锁住成功继续干活,没有锁住就一直等待
'''
会造成线程安全问题的代码
'''
mutex.release() # 释放锁
锁(互斥锁)
- 某段代码某时间只有持有锁的线程才能执行
- mutex.acquire():使用锁
- mutex.release():关闭锁
如何解决死锁问题
mutex.acquire()
mutex2.acquire()
mutex2.release()
mutex.release()
RLOCK:递归锁(为了解决单线程死锁问题)
locks = {
lock1:key1;
lock2:key2;
lock3:key3;
}
创建线程的三种风格
第一种(函数方式)
import _thread 多线程包
import win32api
def show(i):
win32api.MessageBox(0, "我是内容", "我是标题", 1)
# 元组用于传递参数
_thread.start_new_thread(show, (i,))
第二种(类方式)
import threading
import time
import win32api
class Mythread(threading.Thread):
def run(self):
win32api.MessageBox(0, "我是内容", "我是标题", 1)
for i in range(5):
t=Mythread() # 初始化
t.start() # 开启
第三种(匿名方式)
threading.Thread(target=show,args=()).start()
threading.Thread(target=show,args=()).start()
threading.Thread(target=show,args=()).start()
限制线程数量
# 相当于锁,只不过这个是限制执行该代码的线程数量
sem = threading.Semaphore(2)
sem = threading.BoundedSemaphore(2) # 两个方法差不多
sem.acquire()
代码
sem.release()
import threading, time
# 限制线程数量为2个
sem = threading.Semaphore(2)
def gothread():
with sem: # 锁定某段代码所能容纳线程数量
for i in range(10):
print(threading.current_thread().name, i)
time.sleep(1)
for i in range(5):
threading.Thread(target=gothread).start()
线程数量
- sem = threading.Semaphore(2)
- sem = threading.BoundedSemaphore(2) # 两个方法差不多
- 没有足够的线程也会执行,执行完不会等待线程一起结束,谁执行完成谁先结束
凑固定数量线程执行
import threading, time
bar=threading.Barrier(3) # 等待3个线程一起才执行
def sever():
bar.wait()
print(threading.current_thread().name, "start")
time.sleep(5)
print(threading.current_thread().name, "end")
for i in range(5):
threading.Thread(target=sever).start()
Barrier方法介绍
- bar.wait()方法执行必须等到设置数量的线程一起才会一起执行
线程间通信Event
import threading
import time
def goevent():
e=threading.Event() # 事件
def go():
e.wait()
e.clear()
print("go")
threading.Thread(target=go).start()# 开启一个线程
return e
t=goevent()
time.sleep(25)
t.set() # 激发事件
线程间通信介绍
- 事件:e.threading.Event() # 创建事件
- 等待:e.wait() 没有走set就会一直等待,每一个wait都需要一个set激发
- 清理:e.clear() 重置等待,不清理下次就不会等待了,会直接执行
- 激发:e.set()这一步执行完成之后,e.wait()等待才会继续
- 判断是否激发事件:e.is_set()返回是否激发事件的状态
线程通信与事件Condition
import threading, time
def go1():
with cond:
for i in range(10):
time.sleep(1)
print(threading.current_thread().getName(), i)
cond.notify() # 通知
def go2():
with cond:
for i in range(10):
time.sleep(1)
print(threading.current_thread().getName(), i)
if i==5:
cond.wait() # 通知
cond = threading.Condition()
threading.Thread(target=go2).start()
threading.Thread(target=go1).start()
线程通信与事件 Condition=LOCK+Event
- 线程条件变量
- cond.notify() # 通知,不会通知自己
- cond.wait() # 等待通知
- wait必须有同一个notify通知之后才会走
生产者消费者模式
生产者消费者模式代码
import threading
import time
import queue
import random
class createThread(threading.Thread):
def __init__(self,index,myqueue):
threading.Thread.__init__(self)
self.index = index
self.myqueue = myqueue
def run(self):
while True:
time.sleep(3)# 3秒生产一个
num = random.randint(1,1000000) # 随机数
self.myqueue.put("input 生产者"+str(self.index)+"硅胶娃娃"+str(num)+"号")
print("input 生产者"+str(self.index)+"硅胶娃娃"+str(num)+"号")
self.myqueue.task_done()
class buyThread(threading.Thread):
def __init__(self,index,myqueue):
threading.Thread.__init__(self)
self.index = index
self.myqueue = myqueue
def run(self):
while True:
time.sleep(1)
item = self.myqueue.get() # 抓取一个数据
if item is None:
break
print("客户",self.index,"买到物品",item)
self.myqueue.task_done()
myqueue = queue.Queue(10) # 最大容量为10个
for i in range(3):
createThread(i,myqueue).start() # 生产者
for i in range(8):
buyThread(i,myqueue).start()
生产者消费者模式分析
- 生产的速度可以小于于消费的速度
- 生产了一个才能消费一个
- 不会出现线程安全问题
线程池
import time
import threadpool
def show(str):
print("hello", str)
time.sleep(2)
nameList = ["hsgj","hsfdh","hsdh","ffdg"]
start_time = time.time()
pool = threadpool.ThreadPool(10) # 最大容量10个
requests = threadpool.makeRequests(show,nameList) # 构建请求列表: 第一个函数,第二个参数列表
for req in requests:
pool.putRequest(req) # 将请求压入线程池中执行
end_time = time.time()
print(end_time-start_time)
定时任务(定时线程)
import os
import threading
import time
def go():
os.system("start notepad")
timeThread = threading.Timer(5, go) # 延时5秒后执行
timeThread.start()
i = 0
while True:
time.sleep(1)
print(i)
i += 1
定时任务
- threading.Timer(5, go).start()
- 第一个参数定时秒数
- 第二个参数要执行的方法
with在锁中的作用
with作用
lock = threading.Lock() # 创建一个锁
with lock:
pass
with解释
- with会自动打开关闭锁
前台与后台线程(守护线程)
上代码
import threading
import tkinter.messagebox
class MyThread(threading.Thread):
def run(self):
# tkinter.messagebox.showerror(title="错误",message="错误")
for i in range(100):
print(i)
t = MyThread()
t.setDaemon(True) # 设置线程为后台线程
t.start()
前台与后台线程(守护线程)介绍
- threading.Thread默认是前台线程
- 主线程不会等待后台线程(守护线程)
- setDaemon必须放在start之前
- _thread是后台线程
每个线程独立存储
import threading
import time
data = threading.local() # 为每个线程独立存储空间
t1 = lambda x:x+1
t2 = lambda x:x+"1"
def printdata(func,x):
data.x = x
print(id(data.x)) # 不同的地址
for i in rang(5):
data.x=func(data.x)
print(threading.current_thread().name,data.x)
threading.Thread(target=printdata,args=(t1,1)).start()
threading.Thread(target=printdata,args=(t2,"1")).start()
每个线程独立存储
import threading
# 创建全局 ThreadLocal 对象:
localVal = threading.local()
localVal.val = "Main-Thread"
def process_student():
print('%s (in %s)' % (localVal.val, threading.current_thread().name))
def process_thread(name):
# 赋值
localVal.val = name
process_student()
t1 = threading.Thread(target=process_thread, args=('One',), name='Thread-A')
t2 = threading.Thread(target=process_thread, args=('Two',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
print(localVal.val)