• Python 多线程 线程安全、daemon简介 (四)


    线程安全

    只能在Winodws下的ipython中演示,Python命令行、Pycharm、Mac下的ipython都演示不出效果

    import threading
    
    def worker():
        for _ in range(100):
            print("{} is running.".format(threading.current_thread().name))
    
    for i in range(1,5):
        name = "worker-{}".format(i)
        t = threading.Thread(target=worker,name=name)
        t.start()
    

      这里只展示在Windows下ipython中的运行结果:

      对比代码,输出应该是一行一行打印,但是在ipython中有个别行打印时连在了一起。

    print属于线程内的语句,且print函数动作分两部分,打印字符串和打印换行,该线程print拼接换行之前被其它线程打断了,这说明print函数是线程不安全的。

    简单理解,线程不安全就是正在做某事,被打断了。

    线程安全的解决方法:

    1、拼接字符串

    print("{} is running
    ".format(threading.current_thread()),end="")
    

      在print打印字符串时就打印' '换行符,因为字符串是不可变类型,它可以作为一个整体不被分割输出。

    2、logging模块

    logging模块是分级别输出,不允许被打断,是安全的。

    import threading
    import time
    import logging
    logging.basicConfig(level=logging.INFO) #警告级别
    
    def worker():
        for x in range(100):
            # time.sleep(0.5)
            msg = ("{} is running".format(threading.current_thread()))
            logging.info(msg)
    
    for i in range(1):
        t = threading.Thread(target=worker,name='worker-{}'.format(i))
        t.start()
    
    运行结果:
    INFO:root:<Thread(worker-0, started 123145503371264)> is running
    INFO:root:<Thread(worker-0, started 123145503371264)> is running
    INFO:root:<Thread(worker-0, started 123145503371264)> is running
    INFO:root:<Thread(worker-0, started 123145503371264)> is running
    INFO:root:<Thread(worker-0, started 123145503371264)> is running
    INFO:root:<Thread(worker-0, started 123145503371264)> is running
    INFO:root:<Thread(worker-0, started 123145503371264)> is running
    ......
    

      关于logging模块下一篇详细介绍。

    daemon线程与non-daemon线程:

    进程靠线程执行代码,一个进程至少有一个主线程,其它线程都是工作线程。

    主线程是第一个启动的线程。

    父线程:如果线程A启动了一个线程B,A就是B的父线程。

    子线程:B就是A的子线程。

    daemon属性要在start()方法之前设置。

    daemon=None 或者不设置时,  等同于父线程的daemon的值

    daemon=True   父线程退出该线程也退出

    daemon=False  父线程必须等该线程执行完毕。

    class Thread:
    	def __init__(self, group=None, target=None, name=None,
    	                 args=(), kwargs=None, *, daemon=None):
    
    	  if daemon is not None:
    	      self._daemonic = daemon
              else:
                  self._daemonic = current_thread().daemon #默认False

      Python在构造线程时,daemon参数接收值为True或False,默认为None时,等同于父线程的daemon值。

    举例:

    import threading
    import time
    import logging
    logging.basicConfig(level=logging.INFO) #警告级别
    
    def worker():
        for x in range(100):
            time.sleep(1)
            msg = ("{} is running".format(threading.current_thread()))
            logging.info(msg)
    
    t = threading.Thread(target=worker,name='worker-{}'.format(0),daemon=True)
    t.start()
    
    time.sleep(3)
    print('ending')
    
    运行结果:
    INFO:root:<Thread(worker-0, started daemon 123145520332800)> is running
    INFO:root:<Thread(worker-0, started daemon 123145520332800)> is running
    ending
    

      daemon属性为True时,主线程在启动子线程后,继续执行之后语句,主线程执行完毕后,自动退出了所有子线程,主线程也退出。也就是说,当daemon为True时,主线程结束时,子线程无论是否有任务未完成都会随着主线程一起退出。

      当daemon为False时也叫non-daemon,主线程运行完毕后,如果有子线程没有退出,就会等待子线程执行完毕,才会退出主线程,整个程序也就结束了。

    嵌套的daemon线程:

    import threading
    import logging
    logging.basicConfig(level=logging.INFO) #警告级别
    import time
    
    def worker():
        for x in range(10):
            time.sleep(0.3)
            msg = ("{} {} is running".format(x,threading.current_thread()))
            logging.info(msg) #worker不等worker1
            t = threading.Thread(target=worker1,name="worker1-{}".format(x),daemon=True)
            t.start()
            print(x,'~~~~~~~~~~~~~~~~~~~~~')
            # t.join()
    
    def worker1():
        for x in range(1000):
            time.sleep(0.3)
            msg = ("¥¥¥¥¥{} is running".format(threading.current_thread()))
            logging.info(msg)
    
    t = threading.Thread(target=worker,name='worker-{}'.format(0),daemon=False) #主线程等待worker
    t.start()             #  主     子    孙
    # t.join()            # False False True
    time.sleep(1)
    print('ending')
    print(threading.enumerate())
    

      这里很容易绕晕,直接看结论把。

    小结:

    主线程    子线程    子子线程

    False      False      True

    主线程

             启动子线程

                         子进程也启动子线程

      

    再傻瓜点理解就是层次最深的线程的daemon影响整个程序的执行结果,如果没有设置daemon,则取它的父线程的daemon。 下一篇详细介绍。

    小结:

    non-daemon (False) 主线程会等待非daemon线程执行完毕才退出,所以工作完整性请使用non-daemon线程。

    daemon (True) 主线程结束,子线程也会结束。适用于不关心什么时候启动,什么时候结束,丢失一部分数据也不关心的场景。例如:服务、心跳包、监控场景应用居多。

    join()方法:

    正常场景,主线程在起了一个新的子线程后,主线程和子线程是并行的,互不干扰。但是,假如主线程调用了join方法,那它就得等待子线程完全执行完毕才能执行join()之后的语句,相当于又变成了单线程按顺序执行。

    举例:

    import threading
    import time
    
    def worker():
        for i in range(5):
            time.sleep(0.5)
            print('working....')
    
    t = threading.Thread(target=worker)
    t.start()
    # t.join()  #没有使用join()时的运行情况
    print('ending.')
    
    运行结果:
    ending.
    working....
    working....
    working....
    working....
    working....
    

      上面例子中,先打印了主线程的“ending.”语句,说明没有使用join()方法时,主线程线运行完毕,等待子线程也运行完毕才退出程序。

    import threading
    import time
    
    def worker():
        for i in range(5):
            time.sleep(0.5)
            print('working....')
    
    t = threading.Thread(target=worker)
    t.start()
    t.join()   #使用join()方法时
    print('ending.')
    
    运行结果:
    working....
    working....
    working....
    working....
    working....
    ending.
    

      上面例子中,程序先打印的是子线程的语句,子线程运行完毕后,才打印了主线程的“ending.”语句,也就是使用了join()方法后,又变成了面向过程的程序,后一个语句必须要等前一个语句执行完毕才能执行。

      

  • 相关阅读:
    LED调光,PFM即pulse frequence modulation
    调光设备术语:调光曲线(转)
    盗梦陀螺攻略5- PID平衡算法(转)
    连接池中的maxIdle,MaxActive,maxWait参数
    MyBatis 延迟加载,一级缓存,二级缓存设置
    maven常用命令介绍
    科目二倒库的感悟(附一个教练独特的调镜方法)
    科目二怎么调整后视镜
    新手学车上车起步步骤
    ActiveMQ 了解
  • 原文地址:https://www.cnblogs.com/i-honey/p/8043999.html
Copyright © 2020-2023  润新知