守护线程
import time
from threading import Thread
def func():
print('开始执行子线程')
time.sleep(3)
print('子线程执行完毕')
t = Thread(target=func)
t.setDaemon(True) # 进程设置守护进程 是一个属性 daemon = True
t.start()
t2 = Thread(target=func)
t2.start()
t2.join() # 等待t2结束
# 守护线程 守护进程 都是等待主进程或者主线程中的代码 执行完毕
# t2 = Thread(target=func)
# t2.start() ---> 代码执行完毕
# 守护线程就结束了
# 主线程还没结束 等待t2继续执行
# t2执行完毕 主线程结束
# t2 = Thread(target=func)
# t2.start()
# t2.join() # 等待t2结束 执行完这句话代码才执行完毕
# t2线程执行完毕
# 主线程中没有代码了,守护线程结束
锁
import time
from threading import Thread
from threading import Lock
def func():
global n
time.sleep(2)
lock.acquire()
temp = n # 从进程中获取n
time.sleep(0.01)
n = temp-1 # 得到结果,再存储回进程
lock.release()
n = 100
lock = Lock()
t_lst = []
for i in range(100):
t = Thread(target=func)
t.start()
t_lst.append(t)
[t.join() for t in t_lst]
print(n)
# GIL 不是锁数据 而是锁线程
# 在多线程中 特殊情况 仍然要加锁 对数据
# 慢 join lock
死锁
# 科学家吃面
import time
from threading import RLock
from threading import Thread
m = kz = RLock() #
def eat(name):
kz.acquire() # 拿到钥匙
print('%s拿到筷子了'%name)
m.acquire()
print('%s拿到面了'%name)
print('%s吃面'%name)
m.release()
kz.release()
def eat2(name):
m.acquire() # 没有钥匙
print('%s拿到面了' % name)
time.sleep(1)
kz.acquire()
print('%s拿到筷子了' % name)
print('%s吃面' % name)
kz.release()
m.release()
#
# Thread(target=eat,args=('哪吒',)).start()
# Thread(target=eat2,args=('egon',)).start()
# Thread(target=eat,args=('苑昊',)).start()
# Thread(target=eat2,args=('金老板',)).start()
# 死锁
# temp 修改
# n 修改
# 在不同的线程中 恰好要对这两个数据进行操作
# from threading import RLock # 递归锁
# from threading import Lock # 互斥锁
# lock = RLock() # 递归锁
# lock.acquire() # 拿走了一把钥匙
# lock.acquire() # 等着钥匙
# lock.acquire() # 等着钥匙
# print(123)
# lock.release()
# lock.release()
# lock.release()
# 锁 或者 递归锁
# 在多线程并发的情况下,同一个线程中 如果出现多次acquire 就可能产生死锁线程现象
# 用递归锁就能避免
信号量
# 信号量
# 事件
# 条件
# 定时器
# 队列
import time
import random
from threading import Thread
from threading import Semaphore
def func(n,sem):
sem.acquire()
print('thread -%s start'%n)
time.sleep(random.random())
print('thread -%s done' % n)
sem.release()
sem = Semaphore(5) # 一把锁有5把钥匙
for i in range(20):
Thread(target=func,args=(i,sem)).start()
# 信号量 和 线程池 有什么区别?
# 相同点 在信号量acquire之后,和线程池一样 同时在执行的只能有n个
# 不同点
# 开的线程数不一样 线程池来说 一共就只开5个线程 信号量有几个任务就开几个线程
# 对有信号量限制的程序来说 可以同时执行很多线程么?
# 实际上 信号量并不影响线程或者进程的并发,只是在加锁的阶段进行流量限制
事件
# Event
# flag 标志
# 刚刚创建的时候 flag = False
# wait() flag=False 阻塞
#flag = True 非阻塞
# set() False --> True
# clear() True --> False
# 连接mysql数据库
# 我连接三次数据库
# 每0.5秒连接一次
# 创建一个事件 来标志数据库的连接情况
# 如果连接成功,就显示成功
# 否则 就报错 主动抛异常TimeoutError
import time
import random
from threading import Event
from threading import Thread
def conn_mysql(): # 连接数据库
count = 1
while not e.is_set(): # 当事件的flag为False时才执行循环内的语句
if count>3:
raise TimeoutError
print('尝试连接第%s次'%count)
count += 1
e.wait(0.5) # 一直阻塞变成了只阻塞0.5
print('连接成功') # 收到check_conn函数内的set指令,让flag变为True跳出while循环,执行本句代码
def check_conn():
'''
检测数据库服务器的连接是否正常
'''
time.sleep(random.randint(1,2)) # 模拟连接检测的时间
e.set() # 告诉事件的标志数据库可以连接
e = Event()
check = Thread(target=check_conn)
check.start()
conn = Thread(target=conn_mysql)
conn.start()
条件
import threading
def run(n):
con.acquire()
con.wait() # 等着
print("run the thread: %s" % n)
con.release()
if __name__ == '__main__':
con = threading.Condition() # 条件 = 锁 + wait的功能
for i in range(10):
t = threading.Thread(target=run, args=(i,))
t.start()
while True:
inp = input('>>>')
if inp == 'q':
break
con.acquire() # condition中的锁 是 递归锁
if inp == 'all':
con.notify_all()
else:
con.notify(int(inp)) # 传递信号 notify(1) --> 可以放行一个线程
con.release()
定时器
from threading import Timer
def hello():
print("hello, world")
while True: # 每隔一段时间要开启一个线程
t = Timer(10, hello) # 定时开启一个线程,执行一个任务
# 定时 : 多久之后 单位是s
# 要执行的任务 :函数名
t.start()
# sleep的时间
# sleep的时间短 就在线程内while True
# sleep的时间长 就在主线程while True
队列
import queue
# q = queue.Queue() # 队列 线程安全的
# q.get()
# q.put()
# q.qsize()
# lfq = queue.LifoQueue() # 后进先出 :栈
# lfq.put(1)
# lfq.put(2)
# lfq.put(3)
# lfq.put(4)
# print(lfq.get())
# print(lfq.get())
# print(lfq.get())
# print(lfq.get())
# []
import queue
pq = queue.PriorityQueue() # 值越小越优先,值相同就asc码小的先出
pq.put((1,'z'))
pq.put((1,'b'))
pq.put((15,'c'))
pq.put((2,'d'))
#
print(pq.get())
print(pq.get())
concurrent模块
import time
import random
from concurrent import futures
def funcname(n):
print(n)
time.sleep(random.randint(1,3))
return n*'*'
def call(args):
print(args.result())
thread_pool = futures.ThreadPoolExecutor(5)
# thread_pool.map(funcname,range(10)) # map,天生异步,接收可迭代对象的数据,不支持返回值
# f_lst = []
# for i in range(10):
# f = thread_pool.submit(funcname,i) # submit 合并了创建线程对象和start的功能
# f_lst.append(f)
# thread_pool.shutdown() # close() join()
# for f in f_lst: # 一定是按照顺序出结果
# print(f.result()) #f.result()阻塞 等f执行完得到结果
# 回调函数 add_done_callback(回调函数的名字)
thread_pool.submit(funcname,1).add_done_callback(call)
# 统一了入口和方法 简化了操作 降低了学习的时间成本
# 作业一 —— 线程版本的生产者消费者模型 多线程+Queue
# 作业二 —— 爬取网页 线程池实现
# 4核
# 进程4-5个+线程 20个线程 = 100
# 线程池 和 进程池
# 是用来做池操作的最近新的模块
# 开启线程需要成本 成本比开启进程要低
# 高IO的情况下 开多线程
# 所以我们也不能开启任意多个线程
# 开启线程池
# futures.ThreadPoolExecutor # 线程池
# futures.ProcessPoolExecutor # 进程池
# 进程池 cpu个数 + 1
# 线程池 默认 :cpu个数 * 5 4核*5 20个线程