第三十三天- 线程创建、join、守护线程、死锁

 

1.线程,线程创建

  概念:在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程,线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。流水线的工作需要电源,电源就相当于cpu。

  所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位

  多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。

 

  创建:

  线程创建方式一:

 1 from multiprocessing import Process
 2 from threading import Thread
 3 
 4 
 5 def func(n):
 6     print('xxxxx')
 7     print('>>>',n)
 8 
 9 
10 if __name__ == '__main__':
11 
12     # p = Process(target=func,args=(1,))
13     # p.start()
14 
15     t = Thread(target=func,args=(1,))  # 直接创建
16     t.start()
17 
18     print('主线程结束')
View Code

  面向对象创建:

 1 from threading import Thread
 2 
 3 
 4 # 面向对象创建
 5 class Mythread(Thread):  # 继承Thread父类
 6 
 7     def __init__(self,xx):
 8         super().__init__()
 9         self.xx = xx
10 
11     def run(self):  # 必须有run,覆盖父类run中的pass
12         print(self.xx)
13         print('我重置父类方法')
14 
15     def func1(self): # 写其他方法
16         print('我是func1')
17 
18 
19 if __name__ == '__main__':
20 
21     t1 = Mythread('xx')
22     t1.start()  # 默认执行run
23     t1.func1()  # 调用其他方法
24 #
25 
26 from multiprocessing import Process
27 from threading import Thread
View Code

 

 

2.Thread类方法

  join方法:

  主线程等待join子线程执行完毕后才执行

 1 import time
 2 from threading import Thread
 3 
 4 
 5 def func(n):
 6     time.sleep(n)
 7     print('我是子线程')
 8 
 9 
10 if __name__ == '__main__':
11 
12     t = Thread(target=func,args=(1,))
13     t.start()
14 
15     t.join() # 等待子线程执行结束
16     print('我是主线程,子线程结束再执行我')
join

  

  其他方法:

 1 ''
 2 Thread实例对象的方法
 3   # isAlive(): 返回线程是否活动的。
 4   # getName(): 返回线程名。
 5   # setName(): 设置线程名。
 6 
 7 threading模块提供的一些方法:
 8   # threading.currentThread(): 返回当前的线程变量。
 9   # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
10   # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
11 '''
 1 import time, threading
 2 from threading import Thread, current_thread
 3 
 4 
 5 def func():
 6     time.sleep(2)
 7     print('子线程,名字是:', current_thread().getName())  # 返回线程名
 8     print('子线程,ID是:', current_thread().ident)  # 返回线程id
 9 
10 
11 if __name__ == '__main__':
12 
13     for i in range(10):
14         t = Thread(target=func, )
15         t.start()
16 
17     print(threading.enumerate())  # 返回一个包含正在运行的list
18     print(threading.activeCount())  # 返回正在运行的线程数量,等同len(threading.enumerate())
19 
20     print('主线程结束')
其他方法示例

 

 

3.守护线程、事件

  守护线程:

  主进程结束,守护进程跟着结束,再执行非守护进程
  主线程要等待所有非守护线程运行结束才会结束(因为他们属于同一进程)
  需注意:运行结束并非终止运行

  xxx.setDaemon(True)  或者 xxx.daemon = True

 1 import time
 2 from threading import Thread
 3 from multiprocessing import Process
 4 
 5 
 6 def func1():
 7     time.sleep(3)
 8     print('任务1结束')
 9 
10 
11 def func2():
12     time.sleep(2)
13     print('任务2结束')
14 
15 
16 if __name__ == '__main__':
17 
18     # p1 = Process(target=func1,)
19     # p2 = Process(target=func2,)
20     # # p1.daemon = True
21     # p2.daemon = True
22     # p1.start()
23     # p2.start()
24     #
25     # print('主进程结束')
26 
27     t1 = Thread(target=func1,)
28     t2 = Thread(target=func2,)
29     # t1.setDaemon(True) # 只打印出 主线程和t2 因为t2时间比t1小
30     t2.setDaemon(True)  # 会正常打印出所有 因为t1时间大于t2
31     t1.start()
32     t2.start()
33 
34     print('主线程结束')
守护线程与守护进程

  

  事件: 

  event.isSet():返回event的状态值;
  event.wait():如果 event.isSet()==False将阻塞线程;
  event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
  event.clear():恢复event的状态值为False。

 1 import time
 2 from threading import Event
 3 
 4 e = Event()  # 默认false状态
 5 print(e.isSet())  # 事件当前状态
 6 
 7 e.set()  # 改变成Ture
 8 print(e.isSet())
 9 
10 print('稍等...')
11 # e.clear()  # 将e的状态改为False
12 e.wait()  # 如果 event.isSet()==False将阻塞线程
13 
14 if e.isSet() == True:
15     time.sleep(1)
16     print('滴滴滴,上车吧...')
事件代码示例

 

 

4.线程间数据问题

  开启一个线程所需要的时间要远小于开启一个进程

 1 import time
 2 from multiprocessing import Process
 3 from threading import Thread
 4 
 5 
 6 def func(i):
 7     return '我是任务%s'%i
 8 
 9 
10 if __name__ == '__main__':
11 
12     # 多线程
13     t_list = []
14     t_start_time = time.time()
15     for i in range(10):
16         t = Thread(target=func,args=(i,))
17         t_list.append(t)
18         t.start()
19 
20     [tt.join() for tt in t_list]
21     t_end_time = time.time()
22     t_dif_time = t_end_time - t_start_time
23 
24     # 多进程
25     p_list = []
26     p_start_time = time.time()
27     for i in range(10):
28         p = Process(target=func,args=(i,))
29         p_list.append(p)
30         p.start()
31 
32     [pp.join() for pp in p_list]
33     p_end_time = time.time()
34     p_dif_time = p_end_time - p_start_time
35     # 结果受cpu影响
36     
37     print('多线程耗时:',t_dif_time)
38     print('多进程耗时:',p_dif_time)
39     print('主线程结束')
40 
41 '''
42 多线程耗时: 0.0020008087158203125
43 多进程耗时: 0.4188823699951172
44 '''
多线程和多进程效率对比

  线程之间共享进程资源(全局变量在多个线程之间共享),但也会导致数据不但全问题

 1 import time
 2 from threading import Thread
 3 
 4 num = 100
 5 
 6 def func():
 7     global num
 8     tep = num  # tep替换 模拟多步操作
 9     time.sleep(0.001)  # 模拟延迟
10     tep -= 1
11     num = tep
12 
13 
14 if __name__ == '__main__':
15 
16     t_list = []
17     for i in range(100):
18         t = Thread(target=func,)
19         t_list.append(t)
20         t.start()
21 
22     [tt.join() for tt in t_list]
23 
24     print('主线程的num',num)  # 打印出不是100可知数据是共享的
25 
26 # 理论上应该是0,但多线程是并发执行的,会出现上一个线程还在运算中时,下一个线程并未等待它返回值
27 # 也拿了原来的值进来运算,所以打印出了91,92,93不等,可知多线程数据是不安全的
28 '''
29 主线程的num 92
30 '''
验证多线程之间数据共享 数据不安全问题

  加锁解决多线程数据不安全问题

 1 import time
 2 from threading import Thread,Lock
 3 
 4 num = 100
 5 def func(lock,i):
 6     global num
 7     lock.acquire()  # 加锁
 8     
 9     tep = num
10     time.sleep(0.001)  # 模拟延迟
11     tep -= 1
12     num = tep
13 
14     lock.release()  # 释放
15 
16 
17 if __name__ == '__main__':
18     t_list = []
19     lock = Lock()
20     for i in range(100):
21         t = Thread(target=func,args=(lock,i))
22         t_list.append(t)
23         t.start()
24 
25     [tt.join() for tt in t_list]
26     print('主线程num',num)
27 
28 '''
29 主线程num 0
30 '''
Lock

 

  信号量Semaphore

 1 import time,random
 2 from threading import Thread,Semaphore
 3 
 4 
 5 def func(i,s):
 6     s.acquire()
 7     print('%s张烧饼'%i)
 8     time.sleep(random.randint(1,3))
 9     s.release()
10     # 出来一个进去一个 始终6个  最后不足6个就都进来了
11 
12 
13 if __name__ == '__main__':
14     s = Semaphore(6)  # 与Lock类似,不过可限制最大连接数,如这里同时只有6个线程可以获得semaphore
15     for i in range(28):
16         t = Thread(target=func,args=(i,s,))
17         t.start()
Semaphore

 

 

5.死锁和递归锁

  死锁现象:有多个锁时,双方互相等待对方释放对方手里拿到的那个锁导致死锁   

 1 import time
 2 from threading import Thread,Lock
 3 
 4 
 5 def func1(lock_A,lock_B):
 6     lock_A.acquire()
 7     print('张全蛋拿到了A锁')
 8     time.sleep(0.5)
 9     lock_B.acquire()
10     print('张全蛋拿到了B锁')
11     lock_B.release()
12     lock_A.release()
13 
14 
15 def func2(lock_A,lock_B):
16     lock_B.acquire()
17     print('赵二狗拿到了B锁')
18     lock_A.acquire()
19     print('赵二狗拿到了A锁')
20     lock_A.release()
21     lock_B.release()
22 
23 
24 if __name__ == '__main__':
25 
26     lock_A = Lock()
27     lock_B = Lock()
28     t1 = Thread(target=func1,args=(lock_A,lock_B,))
29     t2 = Thread(target=func2,args=(lock_A,lock_B,))
30     t1.start()
31     t2.start()
死锁现象

 

  递归锁:RLock 

  RLock管理一个内置的计数器,
  每当调用acquire()时内置计数器-1;
  调用release() 时内置计数器+1;
  计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

 

 

 1 # 递归锁解决死锁现象
 2 import time
 3 from threading import Thread,Lock,RLock
 4 
 5 
 6 def func1(lock_A,lock_B):
 7     lock_A.acquire()
 8     print('张全蛋拿到了A锁')
 9     time.sleep(0.5)
10     lock_B.acquire()
11     print('张全蛋拿到了B锁')
12     lock_B.release()
13     lock_A.release()
14 
15 
16 def func2(lock_A,lock_B):
17     lock_B.acquire()
18     print('赵二狗拿到了B锁')
19     lock_A.acquire()
20     print('赵二狗拿到了A锁')
21     lock_A.release()
22     lock_B.release()
23 
24 
25 if __name__ == '__main__':
26 
27     # lock_A = Lock()
28     # lock_B = Lock()
29     lock_A = lock_B = RLock()
30     t1 = Thread(target=func1,args=(lock_A,lock_B,))
31     t2 = Thread(target=func2,args=(lock_A,lock_B,))
32     t1.start()
33     t2.start()
递归锁解决死锁现象

 

posted @ 2018-12-03 21:33  刹那灯火明  阅读(362)  评论(0编辑  收藏  举报