线程
1.线程
# 线程
"""
进程是系统资源分配的最小单位
线程是计算机中调调的最小单位
一个进程里至少一个主线程
"""
from threading import Thread
from multiprocessing import Process
import time,random,os
# 1.多个线程是异步并发操作
def func(i):
time.sleep(random.random())
print(i,os.getpid())
if __name__ == "__main__":
for i in range(10):
t = Thread(target=func,args = (i,)) # 创建线程对象
t.start()
print(os.getpid())
# 2.多线程之间的数据彼此共享
num = 1000
lst = []
def func():
global num
num -= 1
for i in range(1000):
t = Thread(target = func)
t.start()
lst.append(t)
for i in lst:
i.join()
print(num) # 0
2.自定义线程
# 1. 自定义线程
from threading import Thread,enumerate
import os,time
class My(Thread): # 必须继承Thread
def __init__(self,name):
super().__init__() #必须手动调用父类构造方法,才能添加成员
self.name = name
def run(self):
print("当前进程号{},参数{}".format(os.getpid(),self.name))
if __name__ == "__main__":
t = My("好人")
t.start()
t.join()
print("主进程执行结束...")
# 2.线程相关属性
"""
线程.is_alive() 检测线程是否仍然存在
线程.setName() 设置线程名字
线程.getName() 获取线程名字
1.currentThread().ident 查看线程id号
2.enumerate() 返回目前正在运行的线程列表
3.activeCount() 返回目前正在运行的线程数量
"""
from threading import currentThread as ct
from threading import enumerate ,Thread
import time
def func():
time.sleep(1)
print("当前线程号{}".format(ct().ident))
if __name__ == "__main__":
t = Thread(target = func)
t.start()
lst = enumerate() #线程对象列表
print(lst,len(lst))
res = t.is_alive()
print(res) # True
print(t.getName()) #Thread-1
t.setName("下载线程")
print(t.getName()) # 下载线程
# 3.线程的缺陷
"""
GIL:
全局解释器锁
效果:
同一时间,一个进程下的多个线程只能被一个cpu执行,不能实现线程的并行操作
原因:
1.历史原因
2.python是解释型语言
改善方法:
1.用多进程间接实现线程的并行
2.换一个Pypy,Jpython解释器
目前来看,还不能根本解决;
对于io密集型任务,python 绰绰有余.
"""
3.守护线程
# 守护线程
"""
等待所有线程全部执行结束,终止守护线程,守护所有线程
语法:
线程.setDaemon(True)
"""
from threading import Thread
import time
def func1():
while True:
time.sleep(1)
print(111)
def func2():
time.sleep(3)
print(222)
if __name__ == "__main__":
t1 = Thread(target = func1)
t2 = Thread(target = func2)
t1.setDaemon(True) # 设置守护线程
t1.start()
t2.start()
t2.join()
print("助计算")
4.线程互斥锁Lock 与 信号量Semaphore
# 1.线程数据安全锁 Lock
from threading import Thread,Lock
import time
total = 0
def func1(lock):
global total
lock.acquire() # 加锁方式一
for i in range(10000):
total += 1
lock.release() #解锁
def func2(lock):
global total
with lock: # 加锁方式二 : 自动完成加锁解锁
for i in range(10000):
total -= 1
if __name__ == "__main__":
lst = []
lock = Lock() # 创建锁对象
for i in range(10):
t1 = Thread(target = func1,args = (lock,))
t1.start()
t2 = Thread(target = func2,args = (lock,))
t2.start()
lst.append(t1)
lst.append(t2)
for i in lst:
i.join()
print(total) # 0
# 2.Semaphore : 线程信号量
from threading import Semaphore ,Thread
import time,random
def func(i,sem):
time.sleep(random.random())
with sem: # 上锁
print("{}唱歌".format(i))
time.sleep(random.randrange(2,4))
print("{}走了".format(i))
if __name__ == "__main__":
sem = Semaphore(3) # 创建锁对象,同一时间给3个线程加锁
for i in range(10):
t = Thread(target=func,args=(i,sem))
t.start()
print("主线程结束`~")
5.死锁Lock与递归锁RLock
from threading import Thread,Lock,RLock
import time
# 1.语法上死锁
"""
只上锁,不解锁,程序阻塞
"""
# 2.逻辑上的死锁
"""
两把不同的所同时上锁
"""
lock1 = Lock()
lock2 = Lock()
def eat1(name):
lock1.acquire()
print("{}抢到了面条~~".format(name))
lock2.acquire()
print("{}抢到了筷子~~".format(name))
print("开始吃面条~~")
time.sleep(1)
lock2.release()
print("{}放下了筷子~~".format(name))
lock1.release()
print("{}放下了面条~~".format(name))
def eat2(name):
lock2.acquire()
print("{}抢到了筷子~~".format(name))
lock1.acquire()
print("{}抢到了面条~~".format(name))
print("开始吃面条~~")
time.sleep(1)
lock1.release()
print("{}放下了面条~~".format(name))
lock2.release()
print("{}放下了筷子~~".format(name))
if __name__ == "__main__":
lst1 = ["熊大","熊二"]
lst2 = ["贾英贺","光头强"]
for i in lst1:
Thread(target =eat1,args = (i,)).start()
for i in lst2:
Thread(target =eat1,args = (i,)).start()
# 3.解决办法
"""
解决办法一:
递归锁 : 是专门解决死锁问题
连续上锁形同虚设,可以快速开锁
解决办法二:
尽量使用一把锁解决问题,不要使用嵌套锁,容易逻辑死锁
"""
# 基本语法
lock = RLock() # 创建递归锁
lock.acquire()
lock.acquire()
lock.release()
lock.release()
print(666)
6.事件Event
# 事件Event
"""
wait : 动态加阻塞
is_set : 获取内部成员属性值
clear : 把成员属性值舍尾False
set : 把成员属性值设为True
"""
from threading import Thread ,Event
import time,random
# 连接远程数据库
# 检测线路
def check(e):
print("检测中~")
time.sleep(1)
print("检测账号中~")
time.sleep(1)
print("检测密码中~")
time.sleep(random.randrange(1,4))
e.set() # 检测OK,放行
# 连接线程
def connect(e):
sign = False
for i in range(1,4):
e.wait(1) # 最多阻塞1秒
if e.is_set():
print("连接成功~~")
sign = True
break
else:
print("连接第{}失败~~".format(i))
# 如果没成功,抛出超时异常
if sign ==False:
raise TimeoutError
if __name__ == "__main__":
e = Event() # 创建事件对象
t = Thread(target=check,args = (e,)).start()
t = Thread(target=connect,args = (e,)).start()
7.线程队列
# 线程队列
"""
put 存放数据 超出队列长度阻塞
get 获取数据 超出队列长度阻塞
put_nowait 存放数据 超出队列长度报错
get_nowait 获取数据 超出队列长度报错
"""
# 1.Queue 队列 : 先进先出,后进后出
from queue import Queue
q = Queue()
q.put(11)
q.put(22)
print(q.get()) #11
print(q.get()) #22
# print(q.get()) #程序阻塞,队列没有数据了
q = Queue(2) # 队列最多存放2个元素
q.put(1)
q.put(2)
# q.put(3) # 阻塞
# 2.LifoQueue 队列 : 先进后出,后进先出
from queue import LifoQueue
lq = LifoQueue()
lq.put(111)
lq.put(222)
print(lq.get()) # 222
print(lq.get()) # 111
# 3.PriorityQueue 队列
"""
按照优先级排序存放数据 (默认从小到大),获取数据从小到大
可以对数字,字母,容器进行排序
注意:
队列里面只能存放同一类型的数据,不能混杂其他类型数据
"""
from queue import PriorityQueue
# (1) 对数字排序
pq = PriorityQueue()
pq.put(22)
pq.put(11)
pq.put(33)
print(pq.get())
print(pq.get())
print(pq.get())
# (2)多个类型数据
"""
pq.put(11)
pq.put("aa")
# 错误 : '<' not supported between instances of 'str' and 'int'
"""