python摸爬滚打之day33----线程

时间:2021-02-19 23:57:55

1、线程

  能够独立运行的基本单位.

  进程: 进程是资源分配的最小单位;  每一个进程中至少有一个线程.

  线程: 线程是cpu调度的最小单位.

python摸爬滚打之day33----线程

2、创建线程(类似于创建进程)

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread
 3 
 4 def func1(m):
 5     time.sleep(1)
 6     print(">>>>>>")
 7     print(m)
 8 
 9 
10 
11 t = Thread(target=func1, args=(25,))
12 t.start()
13 print("主线程结束")
创建线程1
python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread
 3 
 4 
 5 
 6 class MyThread(Thread):
 7     def __init__(self,numb):
 8         super(MyThread, self).__init__()
 9         self.numb = numb
10 
11     def run(self):
12         print("11111111111111111")
13         print(self.numb)
14 
15 
16 if __name__ == '__main__':
17 
18     t = MyThread(500)
19     t.start()
20     print("主线程结束")
创建线程方式2

3、join()

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread
 3 
 4 def func1(m):
 5     print(m)
 6     time.sleep(5)
 7     print("子线程啦啦啦")
 8 
 9 
10 t = Thread(target=func1, args=(25,))
11 t.start()
12 t.join()            # 等子线程结束后再执行主线程
13 
14 
15 print("主线程啦啦啦啦啦啦")
join()

4、线程提供的几个方法

current_thread().getName()    # 获取当前线程的名字
current_thread().is_alive()     # 判断该线程是否还存活
current_thread().isAlive()      # 判断该线程是否还存活


threading.enumerate()       # 当前存活的线程列表
threading.activeCount()     # 当前存活的所有线程数量
threading.active_count()    # 当前存活着的线程的所有数量
python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time,threading
 2 from threading import Thread,current_thread
 3 
 4 
 5 
 6 def func1():
 7     time.sleep(1)
 8     print(current_thread().is_alive())      # 判断该线程是否还存活
 9     print(current_thread().isAlive())
10     print(f"子线程名字:{current_thread().getName()}")
11 
12 
13 
14 
15 if __name__ == '__main__':
16 
17     for i in range(10):
18         t = Thread(target=func1,name=f"子线程{i}号")
19         t.start()
20     print(threading.enumerate())        # 当前存活的线程列表
21     print(threading.activeCount())     # 当前存活的所有线程数量
22     print(f"主线程名字:{current_thread().getName()}" )    # 当前主线程的名字
23     time.sleep(3)
24     print(threading.active_count())      # 当前存活的线程的所有数量
View Code

5、进程和线程的效率对比

  线程省去了系统给进程分配资源, 销毁进程等时间, 效率上明显提高.

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from multiprocessing import Process
 3 from threading import Thread
 4 
 5 
 6 def func():
 7     print("XXXXXXXXXX")
 8 
 9 
10 if __name__ == '__main__':
11 
12     t_lst = []
13     t_s_tm = time.time()
14     for i in range(100):
15         t = Thread(target=func,)
16         t_lst.append(t)
17         t.start()
18     [tt.join() for tt in t_lst]
19     t_e_tm = time.time()
20     t_dis_tm = t_e_tm - t_s_tm
21     print(f"多线程时间: {t_dis_tm}")     # 多线程时间: 0.01200723648071289
22 
23     p_lst = []
24     p_s_tm = time.time()
25     for i in range(100):
26         p = Process(target=func, )
27         p_lst.append(p)
28         p.start()
29     [pp.join() for pp in p_lst]
30     p_e_tm = time.time()
31     p_dis_tm = p_e_tm - p_s_tm
32     print(f"多进程时间: {p_dis_tm}")     # 多进程时间: 1.9823119640350342
进程线程效率对比

6、线程间数据安全

  线程间的数据是共享的, 当大量线程去访问数据时也会造成数据混乱不安全的现象.

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread
 3 
 4 numb = 100
 5 def func():
 6 
 7     global numb
 8     mid = numb
 9     time.sleep(0.000001)
10     numb = mid - 1
11 
12 
13 
14 
15 if __name__ == '__main__':
16 
17     t_lst = []
18     for i in range(100):
19         t = Thread(target=func,)
20         t_lst.append(t)
21         t.start()
22     [tt.join() for tt in t_lst]
23     print(f"拿到的numb为: {numb}")      # 93 或者其他
线程共享数据造成数据混乱

7、同步锁(类似进程)

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread,Lock
 3 
 4 numb = 100
 5 def func(lok):
 6 
 7     global numb
 8     lok.acquire()
 9     mid = numb
10     time.sleep(0.000001)
11     numb = mid - 1
12     lok.release()
13 
14 
15 
16 if __name__ == '__main__':
17 
18     lok = Lock()
19     t_lst = []
20     for i in range(100):
21         t = Thread(target=func,args=(lok,))
22         t_lst.append(t)
23         t.start()
24     [tt.join() for tt in t_lst]
25     print(f"拿到的numb为: {numb}")          # 0
同步锁

8、死锁

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread,RLock,Lock
 3 
 4 def func1(lock_a, lock_b):
 5     lock_a.acquire()
 6     time.sleep(0.5)
 7     print("线程1拿到了A锁")
 8     lock_b.acquire()
 9     print("线程1拿到了B锁")
10     lock_b.release()
11     lock_a.release()
12 
13 def func2(lock_a, lock_b):
14     lock_b.acquire()
15     print("线程2拿到了B锁")
16     lock_a.acquire()
17     print("线程2拿到了A锁")
18     lock_a.release()
19     lock_b.release()
20 
21 
22 
23 if __name__ == '__main__':
24 
25     lock_a = Lock()
26     lock_b = Lock()
27     t1 = Thread(target=func1,args=(lock_a, lock_b))
28     t2 = Thread(target=func2,args=(lock_a, lock_b))
29     t1.start()
30     t2.start()
31 
32 
33 # 结果
34 # func2拿到了B锁
35 # func1拿到了A锁
36 # 阻塞......  (因为线程2拿到B锁的同时, 线程1也拿到了A锁, 然后线程2要拿A锁, 但是线程1没有释放A锁, 
37 #               同理线程2没有释放B锁, 两个线程就互相等, 一直阻塞着......)
死锁

9、递归锁

  解决死锁的方法: ----> 递归锁

  递归锁内部维持着一个计数器, 当计数器 == 0 时, 各线程开始抢夺递归锁, 当线程每抢着一个锁, 递归锁内部的计数器就 +1, 释放一个就 -1.

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread,RLock,Lock
 3 
 4 def func1(lock_a, lock_b):
 5     lock_a.acquire()
 6     time.sleep(0.5)
 7     print("线程1拿到了A锁")
 8     lock_b.acquire()
 9     print("线程1拿到了B锁")
10     lock_b.release()
11     lock_a.release()
12 
13 def func2(lock_a, lock_b):
14     lock_b.acquire()
15     print("线程2拿到了B锁")
16     lock_a.acquire()
17     print("线程2拿到了A锁")
18     lock_a.release()
19     lock_b.release()
20 
21 
22 
23 if __name__ == '__main__':
24 
25     lock_a = lock_b = Lock()
26     t1 = Thread(target=func1,args=(lock_a, lock_b))
27     t2 = Thread(target=func2,args=(lock_a, lock_b))
28     t1.start()
29     t2.start()
30 
31 """
32 注意: 
33 
34     lock_a, lock_b = RLock()   表示lock_a, lock_b使用的是同一把递归锁
35     lock_a = RLock(), loca_b = Rlock()   表示lock_a使用一把递归锁,lock_b使用另一把递归锁,两个递归锁不一样, 相当于死锁
36     lock_a, lock_b = Lock()    表示lock_a, lock_b 使用同一把普通锁, 锁本身还没有释放, 肯定获取不到的
37 
38 """
递归锁

10、守护线程

  守护线程随主线程的结束而结束. 主线程要等所有非守护线程代码统统运行完毕后, 主线程才运行完毕. 

  什么叫运行完毕?  (结束和运行完毕是两码事)

    进程: 对于进程来说, 运行完毕指的是主进程代码运行完毕;

    线程: 对于线程来说, 运行完毕指得是主线程等待该进程内所有非守护线程代码统统运行完毕后, 主线程才运行完毕.

  守护进程: 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束.

  守护线程: 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。

python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from threading import Thread
 3 
 4 
 5 def func1():
 6     time.sleep(3)
 7     print("子线程1结束了")
 8 
 9 def func2():
10     time.sleep(2)
11     print("子线程2结束了")
12 
13 
14 if __name__ == '__main__':
15     t1 = Thread(target=func1,)
16     t2 = Thread(target=func2,)
17 
18     t1.daemon = True
19     t2.daemon = True
20 
21     t1.start()
22     t2.start()
23 
24 
25 
26     print("主线程代码结束了")
守护线程
python摸爬滚打之day33----线程python摸爬滚打之day33----线程
 1 import time
 2 from multiprocessing import Process
 3 
 4 
 5 def func1():
 6     time.sleep(3)
 7     print("子进程1结束了")
 8 
 9 def func2():
10     time.sleep(2)
11     print("子进程2结束了")
12 
13 
14 if __name__ == '__main__':
15     p1 = Process(target=func1,)
16     p2 = Process(target=func2,)
17 
18     p1.daemon = True
19     p2.daemon = True
20 
21     p1.start()
22     p2.start()
23 
24 
25 
26     print("主进程代码结束了")
守护进程

11, 信号量, 事件

  参考进程.