• Python多线程(基本使用、防止重复、杀死线程)


    目录

    一、基本使用

    1.方式一直接用

    2.方式二创建类

    二、守护进程

    1.以第一种方式为例

    2.以第二种方式为例

    三、共享全局变量和互斥锁

    四、防止重复

    五、杀死线程

    六、总结


    由于Python中的多线程受GIL锁的限制,导致不能利用机器多核的特性,只能利用单核,是假的多线程。如果是cpu计算任务,建议使用multiprocessing处理,可以最大限度使用多核cpu。

    多线程的应用场景为IO密集型任务,多线程是能够有效提升运行效率的,这是因为单线程下有IO操作时,会进行IO等待,这样会浪费等待的这段时间,而开启多线程能在线程A等待时,自动切换到线程B,可以减少不必要的时间浪费,从而能提升程序运行效率,但是也不是最好的选择,对于处理IO密集型任务,在Python还有更好的选择协程。

    其实在平时,还有一种情况下会使用线程,就是需要执行无限任务时,这种线程应用不是很多。

    一、基本使用

    1.方式一直接用

    1. import threading
    2. def task():
    3. # 线程的任务
    4. print('111')
    5. threading.Thread(target=task, name='dance').start()

    2.方式二创建类

    1. import threading
    2. class MyThread(threading.Thread):
    3. def __init__(self):
    4. super().__init__()
    5. # self.num = num # 自定义参数
    6. def run(self):
    7. # 线程的任务
    8. print('111')
    9. return 'ok'
    10. if __name__ == '__main__':
    11. my = MyThread()
    12. # 如果有参数
    13. # my = MyThread(num=1)
    14. my.start()

    二、守护进程

    在一个含有线程的python程序中,主线程的代码执行完毕,如果还有其他子线程还未执行完毕,那么主线程会等待子线程执行完毕之后,再结束;

    如果有一个线程必须设置为无限循环,那么该线程不结束,意味着整个python程序就不能结束,那为了能够让python程序正常退出,将这类无限循环的线程设置为守护线程,当程序当中仅仅剩下守护线程时,python程序就能够正常退出,不必关心这类线程是否执行完毕。

    1.以第一种方式为例

    threading.Thread(target=task, name='task',daemon=True).start()
    1. td = threading.Thread(target=task)
    2. td.setName('task')
    3. td.setDaemon(True)
    4. td.start()

    2.以第二种方式为例

    1. my = MyThread()
    2. # 如果有参数
    3. # my = MyThread(num=1)
    4. my.setName('task')
    5. my.setDaemon(True)
    6. my.start()

    三、共享全局变量和互斥锁

    建议参考:python多线程讲解_V-Sugar的博客-CSDN博客

    1. """多线程共享全局变量"""
    2. import threading
    3. import time
    4. g_num = 100 # 这个要使用global
    5. list = [1,2] # 这个在函数中执行的时候要是不改变他的指向就不需要使用global
    6. def test1():
    7. """改变全局变量"""
    8. global g_num
    9. g_num += 1
    10. list.append(1)
    11. print('-----test1----g_num:%d----' % g_num)
    12. print(f'list初始:[1,2]检查是否可以在不使用global的情况下使用全局变量:{list}')
    13. def test2():
    14. """打印全局变量g_num,如果共享则打印的全局变量为101"""
    15. global g_num
    16. print('----test2----g_num:%d----' % g_num)
    17. print(f'----test2----list:{list}----')
    18. def main():
    19. t1 = threading.Thread(target=test1)
    20. t2 = threading.Thread(target=test2)
    21. t1.start()
    22. time.sleep(1) # 确保test1先执行
    23. t2.start()
    24. time.sleep(1)
    25. print('----in main g_num = %d----' % g_num)
    26. if __name__ == '__main__':
    27. main()

    四、防止重复

    以第一种方式启动为例,具体代码如下:

    1. import threading
    2. def task():
    3. # 线程的任务
    4. print('111')
    5. def repeat_thread_detection(t_name):
    6. # 判断 tName线程是否处于活动状态
    7. for item in threading.enumerate():
    8. print('线程列表', item)
    9. if t_name == item.name: # 如果名字相同,说明tName线程在活动的线程列表里面
    10. return True
    11. return False
    12. if not repeat_thread_detection('task'):
    13. threading.Thread(target=task, name='task').start()
    14. else:
    15. print('task线程还处于活动状态,请勿启动新的实例')

    五、杀死线程

    我们使用线程后,很多时候线程任务完成就会随着主线程结束。但是如果我们运行的线程是一个不会结束的线程,在调试使用过程中多次执行,这时候经常会发现线程的结果还是上一次的或者打印出了两次或多次的结果。这是因为不死线程在再次执行后,前一次的线程没有被新执行的任务取代,而是又新建立了一个进程,在新的进程中开启了新的线程。此时,多个线程在同时运行。

    此处以第二种使用方式为例

    1. import os
    2. import threading
    3. def kill(pid):
    4. # 本函数用于中止传入pid所对应的进程
    5. if os.name == 'nt':
    6. # Windows系统
    7. cmd = 'taskkill /pid ' + str(pid) + ' /f'
    8. try:
    9. os.system(cmd)
    10. print(pid, 'killed')
    11. except Exception as e:
    12. print(e)
    13. elif os.name == 'posix':
    14. # Linux系统
    15. cmd = 'kill ' + str(pid)
    16. try:
    17. os.system(cmd)
    18. print(pid, 'killed')
    19. except Exception as e:
    20. print(e)
    21. else:
    22. print('Undefined os.name')
    23. class MyThread(threading.Thread):
    24. def __init__(self):
    25. super().__init__()
    26. # self.num = num # 自定义参数
    27. def run(self):
    28. # 线程的任务
    29. print('111')
    30. return 'ok'
    31. if __name__ == '__main__':
    32. # 1.读取上次保存的pid
    33. f1 = open(file='feishu_pid.txt', mode='r')
    34. pid = f1.read()
    35. f1.close()
    36. # 2.如果存在杀死上一次的进程
    37. print('上一次进程', pid)
    38. if pid:
    39. # 调用kill函数,终止进程
    40. kill(pid=pid)
    41. # 3.获取当前进程的pid
    42. pid = os.getpid()
    43. print('当前进程的pid: ', pid)
    44. # 4.将pid写入本地文件,供下次调用
    45. f2 = open(file='feishu_pid.txt', mode='w')
    46. f2.write(pid.__str__())
    47. f2.close()
    48. # 5.开启新的线程
    49. my = MyThread()
    50. # 如果有参数
    51. # my = MyThread(num=1)
    52. my.start()

    六、总结

    1. 线程是操作系统调度的单位
    2. 线程在不考虑GIL的情况及下切换时需要的资源比较小
    3. 多线程中:如果主线程结束了所有的线程都会结束
  • 相关阅读:
    车载软件架构 --- SOA设计与应用(中)
    基于 golang 从零到一实现时间轮算法 (三)
    什么是接口测试,我们如何实现接口测试?
    行为型设计模式---访问者模式
    大气颗粒物PMF源解析实践技术应用
    MQL语言实现单元测试
    【Vue】避免Vue组件中常见的props默认值陷阱
    Vue 3 相对于 Vue2,模板和组件的一些变化
    vulnhub之momentum
    掌握Linux常用命令,扫平面试需求障碍
  • 原文地址:https://blog.csdn.net/qq_15028721/article/details/126836317