您的位置:首页 > 其它

day34:线程&守护线程&线程锁&事件

2020-08-19 17:52 218 查看

目录

1.线程的基本使用

2.用类定义线程

3.线程相关的函数

4.守护线程

5.线程中安全问题:Lock

6.信号量:Semaphore

7.死锁 互斥锁 递归锁

8.事件:Event

线程的基本使用

首先,明确一下线程和进程的定义

进程:资源分配的最小单位

线程:cpu执行程序的最小单位

1.一个进程资源中可以包含多个线程

def func(num):
time.sleep(random.uniform(0.1,1))
print("当前进程{},参数是{}".format(os.getpid(),num))

for i in range(10): # 在一个进程资源中创建了十个线程
t = Thread(target=func,args=(i,))
t.start()

print(os.getpid())

运行结果如下图所示

2.并发的多线程和多进程谁的速度快? 多线程!

计算多线程执行时间

def func(num):
print("当前进程{},参数是{}".format(os.getpid(), num))

if __name__ == "__main__":
# 多线程
lst = []
# 记录开始时间
startime = time.time()
for i in range(1000):
t = Thread(target=func,args=(i,))
t.start()
lst.append(t)

# 等到所有的子线程执行完毕
for i in lst:
i.join()

# 计算结束时间
endtime = time.time()
print("多线程执行时间",(endtime - startime)) # 0.27

如图所示,跑1000个线程只需要0.27s

计算多进程执行时间

def func(num):
print("当前进程{},参数是{}".format(os.getpid(), num))

if __name__ == "__main__":
lst = []
startime = time.time()
for i in range(1000):
t = Process(target=func, args=(i,))
t.start()
lst.append(t)

# 等到所有的子线程执行完毕
for i in lst:
i.join()

# 计算结束时间
endtime = time.time()
print("多进程执行时间", (endtime - startime))  # 63.30

如图所示,跑1000个进程需要63.30s

所以,我们得出结论!并发的多线程要远比多进程快!!!

3.多线程之间,共享同一份进程资源

num = 1000
def func():
global num
num -= 1

for i in range(1000):
t = Thread(target=func)
t.start()

print(num)

运行结果如下图所示

用类定义线程

class MyThread(Thread):
def __init__(self,name):
# 手动调用父类的构造方法
super().__init__()
self.name = name

def run(self):
time.sleep(1)
print("当前进程号码是{},名字是{}".format(os.getpid() , self.name))

if __name__ == "__main__":
t = MyThread("当前是一个线程")
t.start()
print("主线程执行结束 ... ")

线程相关的函数

1.线程.is_alive() 检测线程是否仍然存在

2.线程.setName() 设置线程名字

3.线程.getName() 获取线程名字

def func():
time.sleep(1)

if __name__ =
56c
= "__main__":
t = Thread(target=func)
t.start()
# 检测线程是否仍然存在
print(t.is_alive()) # True
# 获取线程名字
print(t.getName())  # Thread-1
# 设置线程名字
t.setName("xboyww")
# 获取线程名字
print(t.getName()) # xboyww

1.currentThread().ident 查看线程id号

def func():
print("子线程的线程id{}".format(currentThread().ident))

if __name__ == "__main__":
Thread(target=func).start()
print("主线程的线程id{}".format(currentThread().ident))

运行结果如下图所示

2.enumerate() 返回目前正在运行的线程列表

3.activeCount() 返回目前正在运行的线程数量

def func():
print("子线程的线程id{}".format(currentThread().ident))
time.sleep(0.5)

if __name__ == "__main__":
for i in range(10):
Thread(target=func).start()
lst = enumerate()
# 主线程 + 10个子线程
print(lst, len(lst))

# 3.activeCount() 返回目前正在运行的线程数量
print(activeCount())  # 11

运行结果如下图所示

守护线程

守护线程和守护进程不同

守护进程是守护主进程,主进程结束,守护进程立刻被杀死

守护线程是守护所有线程,必须等所有线程结束,守护线程才会被杀死

def func1():
while True:
time.sleep(0.5)
print("我是func1")

def func2():
print("我是func2 start ... ")
time.sleep(3)
print("我是func2 end ... ")

def func3():
print("我是func3 start ... ")
time.sleep(5)
print("我是func3 end ... ")

if __name__ == "__main__":
t1 = Thread(target=func1)
t2 = Thread(target=func2)
t3 = Thread(target=func3)

# 在start调用之前,设置线程为守护线程
t1.setDaemon(True)

t1.start()
t2.start()
t3.start()

print("主线程执行结束 .... ")

运行结果如下图所示

线程中安全问题:Lock

现在,我们想完成如下的操作:

  1.在线程p1中for循环100万次,每次完成一次+1操作

  2.在线程p2中for循环100万洗,每次完成一次-1操作

根据想法,我们可以写出如下代码:

n = 0

def func1(lock):
global n
for i in range(1000000):
n += 1

# lock.release()

def func2(lock):
global n
for i in range(1000000):
n -= 1

if __name__ == "__main__":
lst = []
lock = Lock()

startime = time.time()
for i in range(10):
t1 = Thread(target=func1, args=(lock,))
t2 = Thread(target=func2, args=(lock,))
t1.start()
t2.start()
lst.append(t1)
lst.append(t2)

for i in lst:
i.join()

endtime = time.time()
print("主线程执行结束 ...  打印{} 时间是{}".format(n, endtime - startime
ad8
))

执行结果如下图所示

然而,这并不是我们想要的结果

所以我们需要在线程t1和线程t2加锁

运行代码,发现得到的结果正是我们想要的结果

信号量:Semaphore

线程的Semaphore和进程的Semaphore完全一致,在此就不过多赘述了

def func(i,sm):
# 上锁 + 解锁
with sm:
print(i)
time.sleep(3)
if __name__ == "__main__":
# 支持同一时间,5个线程上锁
sm = Semaphore(5)
for i in range(20):
Thread(target=func,args=(i,sm)).start()

"""
再创建线程的时候是异步创建
在执行任务时,遇到Semaphore进行上锁,会变成同步程序
"""

死锁 互斥锁 递归锁

1.语法上的死锁

只上锁不解锁,一定会产生死锁

lock = Lock()
lock.acquire()
lock.acquire()
lock.acquire() # 只上锁不解锁,会产生死锁。运行程序会发生阻塞

lock.release()
print(1)

2.逻辑上的死锁

noodle_lock = Lock()
kuaizi_lock = Lock()

def eat1(name):
noodle_lock.acquire()
print("%s 抢到面条了" % (name))
kuaizi_lock.acquire()
print("%s 抢到筷子了" % (name))

print("开始享受面条 ... ")
time.sleep(0.5)

kuaizi_lock.release()
print("%s 放下筷子" % (name))
noodle_lock.release()
print("%s 放下面条" % (name))

def eat2(name):
kuaizi_lock.acquire()
print("%s 抢到筷子了" % (name))
noodle_lock.acquire()
print("%s 抢到面条了" % (name))

print("开始享受面条 ... ")
time.sleep(0.5)

noodle_lock.release()
print("%s 放下面条" % (name))
kuaizi_lock.release()
print("%s 放下筷子" % (name))

if __name__ == "__main__":
name_lst1 = ["Fly", "Hurt"]
name_lst2 = ["Alan", "Cat"]

for name in name_lst1:
Thread(target=eat1, args=(name,)).start()

for name in name_lst2:
Thread(target=eat2, args=(name,)).start()

运行结果如下图所示

3.递归锁

上方的示例造成了逻辑上的死锁现象

想要解决这种情况,我们需要递归锁。

什么是递归锁?

  递归锁专门用来解决这种死锁现象

  临时用于快速解决线上项目发生阻塞死锁问题的

from threading import RLock

rlock = RLock()
rlock.acquire()
rlock.acquire()
rlock.acquire()
rlock.acquire()
print(112233)
rlock.release()
rlock.release()
rlock.release()
rlock.release()

print("程序结束 ... ")

运行结果如下图所示

4.用递归锁解决2中(面条-筷子)的死锁现象

noodle_lock = kuaizi_lock = RLock()

def eat1(name):
noodle_lock.acquire()
print("%s 抢到面条了" % (name))
kuaizi_lock.acquire()
print("%s 抢到筷子了" % (name))

print("开始享受面条 ... ")
time.sleep(0.5)

kuaizi_lock.release()
print("%s 放下筷子" % (name))
noodle_lock.release()
print("%s 放下面条" % (name))

def eat2(name):
kuaizi_lock.acquire()
print("%s 抢到筷子了" % (name))
noodle_lock.acquire()
print("%s 抢到面条了" % (name))

print("开始享受面条 ... ")
time.sleep(0.5)

noodle_lock.release()
print("%s 放下面条" % (name))
kuaizi_lock.release()
print("%s 放下筷子" % (name))

if __name__ == "__main__":
name_lst1 = ["Fly","Hurt"]
name_lst2 = ["Cat","Alan"]

for name in name_lst1:
Thread(target=eat1,args=(name,)).start()

for name in name_lst2:
Thread(target=eat2,args=(name,)).start()

运行结果如下图所示

5.用互斥锁解决2中(面条-筷子)的死锁现象

用互斥锁解决问题,换句话来说,就是尽量用一把锁解决问题

mylock = Lock()
def eat1(name):
mylock.acquire()
print("%s 抢到面条了" % (name))
print("%s 抢到筷子了" % (name))

print("开始享受面条 ... ")
time.sleep(0.5)

print("%s 放下筷子" % (name))
print("%s 放下面条" % (name))
mylock.release()

def eat2(name):
mylock.acquire()
print("%s 抢到筷子了" % (name))
print("%s 抢到面条了" % (name))

print("开始享受面条 ... ")
time.sleep(0.5)

print("%s 放下面条" % (name))
print("%s 放下筷子" % (name))
mylock.release()

if __name__ == "__main__":

name_lst1 = ["A","B"]
name_lst2 = ["C","D"]

for name in name_lst1:
Thread(target=eat1,args=(name,)).start()

for name in name_lst2:
Thread(target=eat2,args=(name,)).start()

事件:Event

e = Event()

wait 动态添加阻塞

clear 将内部的阻塞值改成False

set 将内部的阻塞值改成True

is_set 获取内部的阻塞值状态(True False)

基本语法

e = Event()
print(e.is_set())
e.set()
print(e.is_set())
e.clear()
print(e.is_set())
# 代表最多阻塞3秒
e.wait(3)
print("程序运行中... ")

模拟链接远程数据库

def check(e):
# 用一些延迟来模拟检测的过程
time.sleep(random.randrange(1,6)) # 1 2 3 4 5
# time.sleep(1)
print("开始检测链接用户的合法性")
e.set()

def connect(e):
sign = False
for i in range(1,4): # 1 2 3
# 设置最大等待1秒
e.wait(1)

if e.is_set():
print("数据库链接成功 ... ")
sign
ad8
= True
break
else:
print("尝试链接数据库第%s次失败 ... " % (i))

if sign == False:
# 主动抛出异常,(超时异常)
raise TimeoutError

e = Event()
# 线程1号负责执行连接任务
Thread(target=connect,args=(e,)).start()

# 线程2号负责执行检测任务
Thread(target=check,args=(e,)).start()

运行结果如下图所示

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: