day 22 线程详解
昨日回顾
1.Queue:进程间通信
-实例化得到一个对象
-对象.put()
-对象.get()
2.生产者消费者模型
3.通过共享变量来共享数据(进程间数据是隔离的)
-Manager实现多个进程操作同一个变量
-加锁
4.线程,每个进程下最少有一个线程,cpu调度的最小单位
5.python如何开启线程
今日内容
1.全局解释器锁(GIL)
0 pypy(没有全局解释器锁),比cpython快,但是好多模块用不了 cpython
1.全局解释器锁,GIL锁(cpython解释器的问题)
-当年python设计的时候,还是单核,没有多核的概念
-python需要做垃圾回收(gc)
-垃圾回收线程,进行垃圾回收
-设计了一个大锁(GIL锁),只有拿到这把锁的线程,才能执行
-同一时刻,在一个进程中,可以开多个线程,但是只能有一条线程在执行
-不能利用多核优势
### 只针对于cpython解释器(其他解释器,包括其他语言不这样)
2.如果是计算密集型:要开进程
3.如果是IO密集型:要开线程
2.开启线程的两种方式
from threading import Thread
import time
def task():
time.sleep(1)
print("我是子线程")
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print("我是主线程")
from threading import Thread
import time
class MyThread(Thread):
def __init__(self, a):
self.a = a
super().__init__()
def run(self):
time.sleep(1)
print('我是子线程', self.a)
if __name__ == '__main__':
t = MyThread('lem')
t.start()
print('我是主线程')
3.多线程与多进程比较
3.1 pid比较
3.2 开启速度比较
# 开线程消耗的资源和耗费的时间远远小于开进程
from threading import Thread
import time
from multiprocessing import Process
def task():
time.sleep(1)
print("我是子线程")
if __name__ == '__main__':
t1 = time.time()
t = Thread(target=task)
t.start()
print("我是主线程")
t.join()
print(time.time()-t1)
>>>>>>>>
我是主线程
我是子线程
1.0029711723327637
p = Process(target=task)
p.start()
print("我是主进程")
p.join()
print(time.time()-t1)
>>>>>>>>
我是主进程
我是子线程
1.5179076194763184
3.3 内存数据的共享问题
from threading import Thread
import time
from multiprocessing import Process
def task():
global n
n = 10
print(n)
if __name__ == '__main__':
n = 100
t = Thread(target=task)
t.start()
t.join()
print("主线程")
print(n)
>>>>>>>>
10
主线程
10
4.Thread类的其他方法
import time
from threading import Thread
import threading
def task():
time.sleep(0.1)
print("我是子线程")
if __name__ == '__main__':
t = Thread(target=task)
t.start()
t1 = Thread(target=task)
t1.start()
# print(t.is_alive()) # 看线程是否存活
# print(t.getName()) # 获取线程的名字
# t.setName('lem') # 设置线程名字
# print(t.getName())
#
# print("主线程")
# time.sleep(0.2)
# print(t.is_alive())
# 主线程中执行返回当前线程对象
# res = threading.currentThread()
# print(res)
# 返回当前进程中正在运行的子线程对象列表
# res = threading.enumerate()
# print(res)
# 返回当前正在运行的线程个数
# res = threading.activeCount()
# print(res)
# 线程id号
res = threading.get_ident()
print("主线程",res)
"""
t.is_alive()
t.getName()
t.setName
threading:木块下的一些方法
res = threading.currentThread()
res = threading.enumerate()
res = threading.activeCount()
res = threading.get_ident()
"""
5.join方法
1.等待子线程执行结束
import time
from threading import Thread
import threading
def task(i):
time.sleep(1)
print(f"我是子线程{i + 1}")
if __name__ == '__main__':
t1 = time.time()
ls = []
for i in range(100):
t = Thread(target=task, args=(i,))
t.start()
ls.append(t)
for t in ls:
t.join()
print("主线程")
print(time.time() - t1)
6.守护线程
from threading import Thread
import time
def task():
time.sleep(2)
print("我是子线程")
if __name__ == '__main__':
t = Thread(target=task)
t.setDaemon(True) # 守护线程如果主线程执行结束,子线程也结束(不执行了)
t.start()
print("主线程结束")
7、同步锁(互斥锁)
## 多个线程操作同一个数据(变量),会出现并发安全的问题
# from threading import Thread,Lock
# import time
# import random
# def task():
# global n
#
#
# ### 临界区(加锁)
# time.sleep(random.random())
# temp=n
# time.sleep(random.random())
# temp=temp-1
# n=temp
#
# ##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换
#
# # n-=1
#
#
# if __name__ == '__main__':
# n=10
# ll=[]
# for i in range(10):
# t=Thread(target=task)
# t.start()
# ll.append(t)
#
# for i in ll:
# i.join()
#
#
# print(n)
###出现了并发安全的问题,加锁解决
from threading import Thread,Lock
import time
import random
def task_lock(lock):
global n
### 临界区(加锁)
with lock:
time.sleep(random.random())
temp=n
time.sleep(random.random())
temp=temp-1
n=temp
##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换
# n-=1
def task_no_lock():
global n
time.sleep(random.random())
temp=n
time.sleep(random.random())
temp=temp-1
n=temp
if __name__ == '__main__':
n=10
lock=Lock()
ll=[]
for i in range(10):
# t=Thread(target=task_lock,args=[lock,])
t=Thread(target=task_no_lock,args=[lock,])
t.start()
ll.append(t)
t.join()
# for i in ll:
# i.join()
print(n)
'''
互斥锁和join的区别
如果使用互斥锁:只锁临界区,只有临界区是串行,其他地方还是并发的
如果使用join,整个过程全变成串行执行
'''
8.信号量
### 信号量可以理解为多把锁,同时允许多个线程来更改数据
from threading import Thread, Semaphore
import time
import random
def task(sm, i):
with sm:
print(f"{i}:正在上厕所")
time.sleep(random.random())
print(f"{i}:结束了")
# 信号量可以理解为多把锁,允许多个线程同时进行操作
sm = Semaphore(5)
for i in range(40):
t = Thread(target=task, args=(sm,i))
t.start()
9.Event事件
"""
Event事件:
一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号
比如一个线程等待另一个线程执行结束再继续执行
"""
from threading import Thread, Event
import time
def girl(event):
print("赵丽颖现在在结婚状态")
time.sleep(1)
# 离婚了,发送信号
print("赵丽颖离婚了")
event.set() # 发送一个信号
def boy(i, event):
print(f"屌丝{i}等待赵丽颖的离婚信号")
event.wait() # 收不到信号之前,一直卡在这
print(f"屌丝{i},收到了离婚信号,开始追")
event = Event()
t = Thread(target=girl, args=(event,))
t.start()
for i in range(10):
t1 = Thread(target=boy, args=(i,event,))
t1.start()
作业
1.基于线程实现高并发的tcp服务端
2.作业 写两条线程,一条线程读一个文件的头1/2,另一个线程读这个文件的后1/2
但是第一个线程读完发送信号第二个线程才能读。
3.通过线程实现模拟抢票(票数放在变量中即可)
4.验证一些GIL锁的存在()
5.研究一下哲学家就餐问题,死锁。
import time
from threading import Thread, Event, Lock
# 1.基于线程实现高并发的tcp服务端
"1 ==============================================================="
"""
import socket
sk = socket.socket()
sk.bind(("localhost", 9000))
sk.listen()
def reply(conn, addr):
while True:
try:
data = conn.recv(1024)
if not data: break
print(addr, data.decode())
conn.send(data.upper())
except Exception as e:
print(e)
break
while True:
conn, addr = sk.accept()
t = Thread(target=reply, args=(conn, addr,))
t.start()
"""
# 2.作业 写两条线程,一条线程读一个文件的头1/2,另一个线程读这个文件的后1/2,但是第一个线程读完发送信号第二个线程才能读。
"2 ==============================================================="
"""
def read_first(event):
print("\n读取前一半\n")
with open('多线程客户端测试.py', 'r', encoding='utf-8') as f:
data = f.read()
res = data[:int(len(data) / 2)]
event.set()
print(res)
def read_last(event):
print("\n等待前一半读完\n")
event.wait()
print("\n开始读取后一半\n")
with open('多线程客户端测试.py', 'r', encoding='utf-8') as f:
data = f.read()
res = data[int(len(data) / 2):]
print(res)
event = Event()
t1 = Thread(target=read_first, args=(event,))
t2 = Thread(target=read_last, args=(event,))
t1.start()
t2.start()
"""
# 3.通过线程实现模拟抢票(票数放在变量中即可)
"3 ==============================================================="
"""
tickets = 2
def get_ticket(lock, i):
global tickets
with lock:
if tickets <= 0:
print("票卖完了")
return
time.sleep(0.1)
print(f"此时还有{tickets}张票")
tickets = tickets -1
print(f"{i}:买票成功")
# lock.release()
lock = Lock()
for i in range(10):
t = Thread(target=get_ticket, args=(lock,i,))
t.start()
"""
# 4.验证一些GIL锁的存在()
"4 ==============================================================="
"""
def task():
a = 0
while True:
a = a + 1
t1 = Thread(target=task)
t2 = Thread(target=task)
t3 = Thread(target=task)
t4 = Thread(target=task)
t1.start()
t2.start()
t3.start()
t4.start()
"""
# 5.研究一下哲学家就餐问题,死锁。
"5 ==============================================================="
# import json
# import ujson
# import time
#
#
# def cost_time(func):
# def inner(*args, **kwargs):
# start_time = time.time()
# result = func(*args, **kwargs)
# stop_time = time.time()
# print(stop_time - start_time)
# # print result
# return result
#
# return inner
#
#
# a = {}
# for i in range(1, 1000000):
# a[i] = 1
#
#
# @cost_time
# def json_dumps(obj):
# return json.dumps(obj)
#
#
# @cost_time
# def ujson_dumps(obj):
# return ujson.dumps(obj)
#
#
# r1 = json_dumps(a)
# r2 = ujson_dumps(a)