并发编程 - 线程 - 1.互斥锁/2.GIL解释器锁/3.死锁与递归锁/4.信号量/5.Event事件/6.定时器

Posted Alice的小屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程 - 线程 - 1.互斥锁/2.GIL解释器锁/3.死锁与递归锁/4.信号量/5.Event事件/6.定时器相关的知识,希望对你有一定的参考价值。

1.互斥锁:
原理:将并行变成串行
精髓:局部串行,只针对共享数据修改
保护不同的数据就应该用不用的锁
 1 from threading import Thread, Lock
 2 import time
 3 
 4 n = 100
 5 
 6 def task():
 7     global n
 8     mutex.acquire()  # 效率低了 但是数据安全了
 9     temp = n
10     time.sleep(0.1)  # 100个线程 都拿到了100  所以就是 100个线程100-1
11     n = temp - 1
12     mutex.release()
13 
14 
15 if __name__ == \'__main__\':
16     mutex = Lock()
17     t_l = []
18     for i in range(100):
19         t = Thread(target=task)
20         t_l.append(t)
21         t.start()
22 
23     for t in t_l:
24         t.join()
25 
26     print(\'\', n)
27 """
28 主 99   原因: 100个线程 都拿到了100  所以就是 100个线程100-1  数据不安全 效率高但是不安全
29           要将并行改为串行 
30 """
31 """
32 主 0    原因:效率低了 但是数据安全了
33 """
2.GIL: global interpreter lock
python3 test.py
ps aux | grep test # linux
tasklist | findstr python # windows python.exe

运行python 会有几步:
1.会有一个进程,进程内存空间 python解释器的代码先加载到内存空间
2.test.py 内容加载到内存
3.解释执行;代码交给了python解释器
线程干活指向了python代码 python代码当作参数传给了解释器
线程拿到解释器的代码,拿着python代码当作参数,执行
垃圾回收线程运行解释器的代码
垃圾回收线程和某一个线程冲突了,数据不安全,

 


开多个进程,GIL就没影响了, cpython解释器垃圾回收线程定期启动一个
GIL:互斥锁,保证数据的安全 对CPython解释器,同一时间只有一个线程运行
GIL.acquire() 这样垃圾线程和线程就不会冲突了,这样回收机制就变得安全了
GIL.release()
python解释器,多线程有GIL存在,保证了一个进程下面多个线程的执行是一个一个执行的

有GIL与自动的锁的工作原理:

 


总结:
1.GIL 一个进程内的多个线程同一时间只能运行一个线程,垃圾回收线程是安全的
2.针对不同的数据,就应该加不同的锁,解释器级别的GIL锁,只能保护解释器级别的数据,
不能保护自己的数据,针对自己的共享数据还要加锁;

线程首先抢的是;GIL锁,之后才是mutex
官网:
结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

GIL的存在:同一时刻,只能有一个线程在运行
多核,多进程,但进程开销大,多线程,又不能用多核 ?

cpu干计算的,多个cpu
1.如果是干计算的操作,多核省时间
2.如果干IO阻塞型操作,多核没用

程序运行:都会干计算和IO操作

四个任务:
1.1个核:开多线程 ,因为多进程能用上多核
2.多核:
计算密集型:用多进程,用多核,eg:金融行业的,计算比较多,虽然多进程开销大,但多核,保证了计算快
IO密集型:用多线程,同一时间只能用一个核,1个核一个进程,多线程就在一个核上来回切和四个核来回切是一样的

现在写的软件:
网络打交道,网络的IO
IO密集型,用多线程
 1 """
 2 计算密集型应该用: 多进程 效率高  
 3 """
 4 from multiprocessing import Process
 5 from threading import Thread
 6 import os,time
 7 
 8 def work():
 9     res=0
10     for i in range(100000000):
11         res*=i
12 
13 
14 if __name__ == \'__main__\':
15     l=[]
16     print(os.cpu_count()) #本机为8核
17     start=time.time()
18     for i in range(8):
19         # p=Process(target=work) #耗时8s多
20         p=Thread(target=work) #耗时37s多
21         l.append(p)
22         p.start()
23     for p in l:
24         p.join()
25     stop=time.time()
26     print(\'run time is %s\' %(stop-start))
27 
28 """
29 IO密集型:多线程 效率高
30 """
31 from multiprocessing import Process
32 from threading import Thread
33 import threading
34 import os,time
35 def work():
36     time.sleep(2)
37     print(\'===>\')
38 
39 if __name__ == \'__main__\':
40     l=[]
41     print(os.cpu_count()) #本机为8核
42     start=time.time()
43     for i in range(400):
44         # p=Process(target=work) #耗时8s多,大部分时间耗费在创建进程上
45         p=Thread(target=work) #耗时2s多
46         l.append(p)
47         p.start()
48     for p in l:
49         p.join()
50     stop=time.time()
51     print(\'run time is %s\' %(stop-start))
3.死锁:
你拿着我的锁,我拿着你的锁

互斥锁:Lock()
互斥锁只能acquire一次

递归锁:RLock()
可以连续acquire多次,每acquire一次计数器+1,
只有计数为0时,才能被抢到acquire
 1 from threading import Thread,Lock
 2 import time
 3 
 4 mutexA=Lock()
 5 mutexB=Lock()
 6 
 7 class MyThread(Thread):
 8     def run(self):
 9         self.f1()
10         self.f2()
11 
12     def f1(self):
13         mutexA.acquire()
14         print(\'%s 拿到了A锁\' %self.name)
15 
16         mutexB.acquire()
17         print(\'%s 拿到了B锁\' %self.name)
18         mutexB.release()
19 
20         mutexA.release()
21 
22 
23     def f2(self):
24         mutexB.acquire()
25         print(\'%s 拿到了B锁\' % self.name)
26         time.sleep(0.1)
27 
28         mutexA.acquire()
29         print(\'%s 拿到了A锁\' % self.name)
30         mutexA.release()
31 
32         mutexB.release()
33 
34 if __name__ == \'__main__\':
35     for i in range(10):
36         t=MyThread()
37         t.start()
38 """
39 Thread-1 拿到了A锁  # 死锁了 卡住了
40 Thread-1 拿到了B锁
41 Thread-1 拿到了B锁
42 Thread-2 拿到了A锁
43 """
 1 # 互斥锁只能acquire一次
 2 # from threading import Thread,Lock
 3 #
 4 # mutexA=Lock()
 5 #
 6 # mutexA.acquire()
 7 # mutexA.release()
 8 
 9 # 递归锁:可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire
10 from threading import Thread,RLock
11 import time
12 
13 mutexB=mutexA=RLock()
14 
15 class MyThread(Thread):
16     def run(self):
17         self.f1()
18         self.f2()
19 
20     def f1(self):
21         mutexA.acquire()
22         print(\'%s 拿到了A锁\' %self.name)
23 
24         mutexB.acquire()
25         print(\'%s 拿到了B锁\' %self.name)
26         mutexB.release()
27 
28         mutexA.release()
29 
30 
31     def f2(self):
32         mutexB.acquire()
33         print(\'%s 拿到了B锁\' % self.name)
34         time.sleep(2)
35 
36         mutexA.acquire()
37         print(\'%s 拿到了A锁\' % self.name)
38         mutexA.release()
39 
40         mutexB.release()
41 
42 if __name__ == \'__main__\':
43     for i in range(10):
44         t=MyThread()
45         t.start()
46 """
47 Thread-1 拿到了A锁  # 解决了 死锁
48 Thread-1 拿到了B锁
49 Thread-1 拿到了B锁
50 Thread-1 拿到了A锁
51 Thread-2 拿到了A锁
52 Thread-2 拿到了B锁
53 Thread-2 拿到了B锁
54 Thread-2 拿到了A锁
55 Thread-4 拿到了A锁
56 Thread-4 拿到了B锁
57 Thread-5 拿到了A锁
58 Thread-5 拿到了B锁
59 Thread-5 拿到了B锁
60 Thread-5 拿到了A锁
61 Thread-7 拿到了A锁
62 Thread-7 拿到了B锁
63 Thread-7 拿到了B锁
64 Thread-7 拿到了A锁
65 Thread-9 拿到了A锁
66 Thread-9 拿到了B锁
67 Thread-9 拿到了B锁
68 Thread-9 拿到了A锁
69 Thread-3 拿到了A锁
70 Thread-3 拿到了B锁
71 Thread-3 拿到了B锁
72 Thread-3 拿到了A锁
73 Thread-6 拿到了A锁
74 Thread-6 拿到了B锁
75 Thread-6 拿到了B锁
76 Thread-6 拿到了A锁
77 Thread-10 拿到了A锁
78 Thread-10 拿到了B锁
79 Thread-10 拿到了B锁
80 Thread-10 拿到了A锁
81 Thread-8 拿到了A锁
82 Thread-8 拿到了B锁
83 Thread-8 拿到了B锁
84 Thread-8 拿到了A锁
85 Thread-4 拿到了B锁
86 Thread-4 拿到了A锁
87 """
4.信号量
  信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,
   信号量同一时间可以有5个任务拿到锁去执行
  信号量:同一时间有多个线程在进行
 1 from threading import Thread,Semaphore,currentThread
 2 import time,random
 3 
 4 sm=Semaphore(1)
 5 
 6 def task():
 7     # sm.acquire()
 8     # print(\'%s in\' %currentThread().getName())
 9     # sm.release()
10     with sm:  # 类似于sm.acquire() # 同一时间可以来3个人,1个人,或者2个人
11         print(\'%s in\' %currentThread().getName())
12         time.sleep(random.randint(1,3))
13 
14 
15 if __name__ == \'__main__\':
16     for i in range(10):
17         t=Thread(target=task)
18         t.start()
19 """
20 Thread-1 in
21 Thread-2 in
22 Thread-3 in
23 
24 Thread-4 in
25 
26 
27 Thread-6 in
28 Thread-5 in
29 Thread-7 in
30 
31 
32 Thread-8 in
33 Thread-9 in
34 
35 Thread-10 in 
36 """
5.Event:
多个线程之间同步的,一个线程告诉另一些线程可以做其他的活了
event.wait()
event.wait(2)
event.set()
event.is_set()
event.clear()
 1 from threading import Thread,Event
 2 import time
 3 
 4 event=Event()
 5 # event.wait()   # 等 ...直到 set
 6 # event.set()
 7 
 8 
 9 def student(name):
10     print(\'学生%s 正在听课\' %name)
11     # event.wait()  # 学生要等7秒 才能下课
12     event.wait(2)   # 学生等2秒 直接下课了
13 
14     print(\'学生%s 课间活动\' %name)
15 
16 
17 def teacher(name):
18     print(\'老师%s 正在授课\' %name)
19     time.sleep(7)
20     event.set()
21 
22 
23 if __name__ == \'__main__\':
24     stu1=Thread(target=student,args=(\'alex\',))
25     stu2=Thread(target=student,args=(\'wxx\',))
26     stu3=Thread(target=student,args=(\'yxx\',))
27     t1=Thread(target=teacher,args=(\'egon\',))
28 
29     stu1.start()
30     stu2.start()
31     stu3.start()
32     t1.start()
33 
34 
35 # ------------------
36 # 设置链接的超时时间
37 from threading import Thread,Event,currentThread
38 import time
39 
40 event=Event()
41 
42 def conn():
43     # print(\'%s is connecting\'%currentThread().getName())
44     # event.wait()
45     # print(\'%s is connected\'%currentThread().getName())
46 
47     n=0
48     while not event.is_set():
49         if n == 3:
50             print(\'%s try too many times\' %currentThread().getName())
51             return
52         print(\'%s try %s\' %(currentThread().getName(),n))
53         event.wait(0.5)
54         n+=1
55 
56     print(\'%s is connected\' %currentThread().getName())
57 
58 
59 def check():
60     print(\'%s is checking\' %currentThread().getName())
61     time.sleep(5)
62     event.set()
63 
64 
65 if __name__ == \'__main__\':
66     for i in range(3):
67         t=Thread(target=conn)
68         t.start()
69     t=Thread(target=check)
70     t.start()
71 """
72 Thread-1 try 0
73 Thread-2 try 0
74 Thread-3 try 0
75 Thread-4 is checking
76 Thread-3 try 1
77 Thread-2 try 1
78 Thread-1 try 1
79 Thread-3 try 2
80 Thread-1 try 2
81 Thread-2 try 2
82 Thread-3 try too many times
83 Thread-2 try too many times
84 Thread-1 try too many times
85 """
6.定时器:Timer
  t=Timer(5,task,args=(\'egon\',))
  t.start()
  t.cancel()
 1 from threading import Timer
 2 
 3 def task(name):
 4     print(\'hello %s\' %name)
 5 
 6 t=Timer(5,task,args=(\'egon\',))  # 就是起了一个线程
 7 t.start()
 8 
 9 # ----------------------
10 from threading import Timer
11 import random
12 
13 class Code:
14     def __init__(self):
15         self.make_cache()
16 
17     def make_cache(self,interval=10):
18         self.cache=self.make_code()
19         print(self.cache)
20         self.t=Timer(interval,self.make_cache)
21         self.t.start()
22 
23     def make_code(self,n=4):
24         res=\'\'
25         for i in range(n):
26             s1=str(random.randint(0,9))
27             s2=chr(random.randint(65,90))
28             res+=random.choice([s1,s2])
29         return res
30 
31     def check(self):
32         while True:
33             code=input(\'请输入你的验证码>>: \').strip()
34             if code.upper() == self.cache:
35                 print(\'验证码输入正确\')
36                 self.t.cancel()
37                 break
38 
39 obj=Code()
40 obj.check()




以上是关于并发编程 - 线程 - 1.互斥锁/2.GIL解释器锁/3.死锁与递归锁/4.信号量/5.Event事件/6.定时器的主要内容,如果未能解决你的问题,请参考以下文章

并发编程目录

GIL全局解释器锁

Python GIL锁 死锁 递归锁 event事件 信号量

GIL

9 并发编程-(线程)-守护线程&互斥锁

GIL锁