threading模块:利用CPU空闲时间执行多任务。Python的多线程实际是遇到IO操作就CPU切换到其它任务。


1、GIL (Global Interpreter Lock):全局解释器锁

        作用就是保证同一时刻CPU只执行一个线程。只有CPython有GIL锁。

 

2、简单的threading使用

常用类和方法名:参数作用示例
threading.enumerate()

用列表,列出所有活动的线程名和ID

示例中列出的是

s = threading.enumerate()

for v in s:

    print(v.name)

threading.Tread.isAlive(线程实例名)
线程实例名:下面的thread_create判断线程是否正在运行,返回True或False

threading.Tread.isAlive(ss)

或者

i.isAlive() #i是创建的Thread实例

thread_create=threading.Thread()

target:加入多线程的对象名

args:对象的参数,元组形式

kwargs:对象的字典参数

name:指定线程名,默认Thread-N

把一个对象实例化成一个线程对象
thread_create = threading.Tread(target=work,args=('tom',),kwargs={'work_name':'eat'})
thread_create.start()
启动线程thread_create.start()
thread_create.join()

timeout:线程阻塞的时间(超时时间

timeout默认,等待此线程执行完毕

等待至线程中止或timeout超时,再继续执行程序

 

thread_create.join(5)

或者

thread_create.join(timeout=5)

thread_create.getName()
获取thread_create线程名thread_create.getName()
thread_create.setName()name:线程名设置thread_create线程名,这个名字只是标识thread_create.setName('th1')




  1. """
  2. 简单的例子
  3. 可以使用两种方法创建多线程
  4. """
  5. #方法一:函数式调用。
  6. import time,random
  7. import threading
  8. work_list = ["睡觉" ,"吃饭""学习" ,"嗯哼"]  # 工作列表
  9. def work(name, cost):
  10.  '''定义函数'''
  11.     print(" 你开始 [31;2m{}[0m".format(name))
  12.     time.sleep(cost)
  13.     print("{} 竟然只用了{}秒钟!".format(name, cost))
  14.  
  15. thread_list = [] # 线程列表
  16. start_time = time.time()  # 记录开始的时间
  17. # 线程开始
  18. for i in work_list:
  19.     #循环读取工作内容
  20.     cost_time= random.randint(5,10)    # 生成随机时间,传输给函数
  21.     # 创建多线程实例
  22.     t = threading.Thread(target = work,args=(i,cost_time))
  23.     thread_list.append(t)      # 把实例内存地址存储到列表,后续使用
  24.     t.start()    # 开启线程
  25. active_thread = threading.enumerate()    #列出当前所有活动的线程
  26. print(active_thread)                     #结果是一个列表
  27. for i in active_thread: 
  28.     print (i.name)         # 调用线程的name属性,表出线程名,实际是调用Thread类析构函数的self.name属性,在方法二里面会看到线程名和工作名是一样的
  29.     print(i.getName())     # 获取当前线程名称
  30.     i.setName("change-name"+i.name)    #改名
  31.     print(i.getName())     # 重新获取线程名字
  32.     
  33. for i in thread_list:  # 从列表里读取线程地址
  34.     i.join()    # 每个线程加入阻塞,默认值等每个线程都结束,继续运行
  35.     
  36. end_time = time.time()  # 记录结束的时间
  37. sum_time = end_time - start_time # 计算总耗时
  38. print ('总用时:{}'.format(sum_time))
  1. '''方法二:重构Thread类的run函数,请注意,只能重构__init__和run函数,其它的不要重构!!!!!'''
  2. import time,random
  3. import threading
  4. '''重写run方法调用'''
  5. work_list = ["睡觉" ,"吃饭""学习" ,"嗯哼"# 工作列表
  6. class work(threading.Thread):
  7. def __init__(self,name,cost):
  8. threading.Thread.__init__(self)
  9. self.name = name
  10. self.cost = cost
  11. def run(self):
  12.                 self.do_work()
  13.         def do_work(self):
  14.                 """工作函数"""
  15.           print(" 你开始 [31;2m{}[0m".format(self.name))
  16. time.sleep(self.cost)
  17. print("{} 竟然只用了{}秒钟!".format(self.name, self.cost)) 
  18. start_time = time.time() # 记录开始的时间
  19. thread_list = []
  20. for i in work_list:
  21. cost_time = random.randint(5,9)
  22. t = work(i, cost_time)
  23. thread_list.append(t)
  24. t.start()
  25. active_thread = threading.enumerate()
  26. for i in active_thread:
  27.         print(i.name) # 打印出来的是工作名称,不是Thread-N这个形式的!!!
  28. for i in thread_list:
  29. i.join()
  30. end_time = time.time() # 记录结束的时间
  31. sum_time = end_time - start_time # 计算总耗时
  32. print ('总用时:{}'.format(sum_time))

 

3、守护线程deamon

 

类、方法、属性名
参数作用示例
setDaemon(BOOL)

BOOL:True

              False

设置线程为父线程的守护线程,必须在strar()线程开始前设置。

m为创建的进程实例。

m.setDaemon(True)

isDaemon()

判断当前线程是否设置了守护线程

返回BOOL型,True 或 False

m.isDaemon()




  1. """守护线程Daemon实例"""
  2. import time,random
  3. import threading
  4. work_list = ["睡觉" ,"吃饭""学习" ,"嗯哼"]  # 工作列表
  5. def work(name, cost):
  6.  '''定义函数'''
  7.  print(" 你开始 [31;2m{}[0m".format(name))
  8.  time.sleep(cost)
  9.  print("{} 竟然只用了{}秒钟!".format(name, cost))
  10. def main_work(name):
  11.  w = threading.Thread(target = work, args=("嗯哼",10))
  12.  w.start()
  13.  w.join(9)
  14.  
  15. start_time = time.time()  # 记录开始的时间
  16. m = threading.Thread(target = main_work, args=('Done',))
  17. m.setDaemon(True)    # 设置守护进程,主线程完成,main_work结束,main_work的子线程也结束
  18. m.start()
  19. m.join(2)
  20. end_time = time.time()
  21. print("你{}了!!!".format("Done"))
  22. print ("总耗时:",end_time-start_time)
  23. #-------------执行结果--------------
  24. 你开始 [31;2m嗯哼[0m
  25. 你Done了!!!
  26. 总耗时: 2.0008544921875
  27. #--因为设置m为守护线程,所以当主线程执行结束后,m和由m生成的所有线程都随着主线程结束。

、线程锁Lock、递归锁Rlock

    加锁以后线程依次运行。


类、方法、属性名参数作用示例
Lock()

Lock类,用于创建Lock对象
lock = threading.Lock()
Rlock()

Rlock类,用于创建递归锁,也叫做多重锁

同一线程可以acquire()多次,但要对应相同数量的release()

rlock = threading.Rlock()
acquire()

Lock和Rlock的方法:

启用线程锁

lock.acquire()
release()

Lock和Rlock的方法:

释放线程锁

lock.release()




  1. ###Lock实例###
  2. import time,random
  3. import threading
  4. work_list = ["睡觉" ,"吃饭""学习" ,"嗯哼"]  # 工作列表
  5. def work(name,cost):
  6.  '''定义函数'''
  7.  lock.acquire()    #线程锁启用
  8.  work_list[name] = name
  9.  time.sleep(cost)
  10.  print("time:{},list:{}".format(cost,work_list))
  11.  lock.release()    #线程锁释放,只有释放后,才能运行其它线程
  12. start_time = time.time()  # 记录开始的时间
  13. thread_list = []
  14. lock = threading.Lock()
  15. for i in range(3):
  16.  m = threading.Thread(target = work, args=(i,3))
  17.  thread_list.append(m)
  18.  m.start()
  19. for i in thread_list:
  20.  i.join()
  21. end_time = time.time()
  22. print ("总耗时:",end_time-start_time)
  23. -----------------加锁版运行结果------------------
  24. time:3,list:[0'吃饭''学习''嗯哼']
  25. time:3,list:[01'学习''嗯哼']
  26. time:3,list:[012'嗯哼']
  27. 总耗时: 9.002515077590942
  28. -----------------不加锁运行结果------------------
  29. time:3,list:[012'嗯哼']
  30. time:3,list:[012'嗯哼']
  31. time:3,list:[012'嗯哼']
  32. 总耗时: 3.002171516418457


、信号量(semaphore)

 

类、方法、属性名参数作用示例
BoundedSemaphore(num)num:int型,只允许num个线程同时运行创建一个semaphore对象

semaphore = threading.BoundedSemaphore(3)

acquire()
把线程加入信号量对象semaphore .acquire()
release()
把线程从信号量对象中删除semaphore .release()




  1. 待测试
  2. import threading,time,random
  3. work_list = ["睡觉" ,"吃饭""学习" ,"嗯哼"]  # 工作列表
  4. def work(name,cost):
  5.  semaphore.acquire()
  6.  print("{}用了{}分钟".format(name,cost))
  7.  time.sleep(cost)
  8.  #print(name)
  9.  semaphore.release()
  10. thread_list=[]
  11. semaphore = threading.BoundedSemaphore(3)
  12. index = ""
  13. for i in work_list:
  14.  index = index+str(work_list.index(i))
  15.  num = random.randint(2,5)
  16.  t = threading.Thread(target=work, args = (i+index,num))
  17.  thread_list.append(t)
  18.  t.start()
  19.  
  20. for j in thread_list:
  21.  j.join()
  22.  
  23. print ("Done")
  24. #------------结果------------
  25. 睡觉0用了2分钟
  26. 吃饭01用了5分钟
  27. 学习012用了4分钟
  28. #以上三个先出现,下面一行后出现,说明了一次只能运行三个线程
  29. 嗯哼0123用了2分钟

 

、定时器(Timer)


类、方法、属性名参数作用示例
Timer(time,func,args)

time:延时时间(单位:秒)

func:要执行的函数名

args:参数,与多进程使用方法一样

生成定时器实例,过time的时长后,执行func的功能。
timer=threading.Timer(3,work,('David',))
start()

启动定时器
timer.start()
stop()

停止定时器
timer.stop()




  1. import threading
  2. def work(name):
  3.      print ("循环:{}".format(name))
  4.      global timer
  5.      timer=threading.Timer(3,work,('David',))
  6.      timer.start()
  7. timer=threading.Timer(3,work,('David',))
  8. timer.start()

、事件(Event)

       Event是线程同步的一种方式,类似于一个标志,当该标志为false时,所有等待该标志的线程阻塞,当为true时,所有等待该标志的线程被唤醒。

       Event是一个重要的概念,有一个编程思想叫事件驱动模型。稍后讨论。


 

类、方法、属性名参数作用示例
Event()

实例化事件对象
e = threading.Event()
isSet()

判断事件标志,返回True或False

e.isSet()

set()

设置事件标志,isSet()为True
e.set()
clear()

清除事件标志,isSet()为Truee.clear()
wait(timeout)
timeout:时间


 

  1. ####事件驱动的红绿灯实例####
  2. import threading
  3. import time
  4. class traffic(threading.Thread):
  5.     '''使用继承类方式写一个进程'''
  6.     def __init__(self,event_obj):
  7.         threading.Thread.__init__(self)
  8.         self.event_obj = event_obj          # 传递事件对象
  9.     def light(self):
  10.         sec = 1         # 秒数
  11.         while True:
  12.             if sec / 1 == 1:                # 初始绿灯
  13.                 self.event_obj.set()        # 设置事件标志
  14.                 print ('green...')
  15.             elif sec / 5 == 1:              # 计数5秒,变黄灯,黄灯可以通行,所以不改变事件标志
  16.                 print ('yellow...')
  17.             elif sec / 7 == 1:              # 计数7秒,变红灯
  18.                 self.event_obj.clear()      # 清除事件标志
  19.                 print('red...')
  20.             elif sec == 10:                 # 到10秒重新计数
  21.                 sec = 1
  22.                 continue
  23.             sec += 1
  24.             time.sleep(1)                   # 延迟
  25.     def run(self):
  26.         # 重写run函数,启动灯。
  27.         self.light()
  28. def car(event_obj):
  29.     # 汽车通行函数
  30.     while True:
  31.         if event_obj.isSet():               # 判断事件标志是True,代表绿灯,打印通行
  32.             print('the car is running!')
  33.         else:                               # 判断事件标志是False,代表红灯,打印通行
  34.             print('the car is stop!')
  35.         time.sleep(1)                       # 延迟
  36. def exit():
  37.     # 定义控制函数,检测鼠标输入,C则结束所有进程,退出程序
  38.     t = traffic(e)                                  # 创建信号灯线程对象
  39.     c = threading.Thread(target=car, args=(e,))     # 创建汽车线程对象
  40.     t.setDaemon(True)                               # 把两个设置成守护线程,跟随exit结束
  41.     c.setDaemon(True)
  42.     t.start()                                       # 线程启动
  43.     c.start()
  44.     while True:                                    # 循环检测键盘输入
  45.         s = input().strip()                         # 读取键盘输入字符
  46.         if s == 'c':                               # 如果为C,结束exit
  47.             print('程序退出!')
  48.             return
  49. e = threading.Event()                       # 创建事件对象
  50. ex=threading.Thread(target=exit)            # 创建exit进程对象
  51. ex.start()
  52. ex.join()



参考资料:

https://blog.csdn.net/drdairen/article/details/60962439

https://www.cnblogs.com/wang-can/p/3582051.html

https://blog.csdn.net/wanghaoxi3000/article/details/70880753\

https://www.cnblogs.com/nulige/p/6297829.html