zoukankan      html  css  js  c++  java
  • 第三十三天- 线程创建、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()
    递归锁解决死锁现象
  • 相关阅读:
    Thoughtworks的技术雷达
    Stackdump: 一个可以离线看stackoverflow的工具
    我最喜欢的visual studio 2013的新特性
    把用octopress最新发布的博文同步到提供metaweblog API的博客(例如博客园)上
    博客搬家到 http://fresky.github.io/
    runnable:在线IDE+代码片段分享
    用LINQPad加上Tx驱动来分析log
    编码规范
    数论欧几里德定理的运用
    uva 11806 容斥原理+dfs
  • 原文地址:https://www.cnblogs.com/xi1419/p/10061053.html
Copyright © 2011-2022 走看看