进程和线程二--线程安全

本文详细介绍了Python中的线程安全问题,通过示例展示了如何避免线程不安全的print操作,并推荐使用logging模块。同时,讲解了daemon线程和non-daemon线程的概念及其应用,强调了线程join方法在等待线程结束时的作用。此外,还介绍了threading.local类用于实现线程间的数据隔离,以及threading.Timer类实现定时任务的延迟执行。

六、线程安全:

多线程中,不同线程的函数,一个线程中的函数未执行完,被暂停插入执行另一个线程。例如:print输出日志,函数分两步,先打印字符串,然后再换行。多线程中,就在这之间发生了线程切换,这就说明print函数是线程不安全。

解决办法:

  1. print打印的字符串,作为不可变类型,作为一个整体不可分割输出。就不让print输出换行符,在字符串末尾加上换行符
  2. 使用logging模块
    案例练习:
import logging
import threading
import time

logging.basicConfig(level=logging.INFO)


def worker(nums):
    for num in range(nums):
        # print('{} is running\n'.format(threading.current_thread().name), end='')
        logging.info(threading.enumerate())
        msg = '{}{} is running'.format(num, threading.current_thread())
        logging.info(msg)
        # print(threading.enumerate())


def worker1(nums):
    for num in range(nums):
        # print('{} is running\n'.format(threading.current_thread().name), end='')
        logging.info(threading.enumerate())
        msg = '$$${}{} is running'.format(num, threading.current_thread())
        logging.info(msg)
        # print(threading.enumerate())

if __name__ == '__main__':
    threading.Thread(target=worker, args=(10, ), name='worker-{}'.format(0), daemon=False).start()
    time.sleep(2)
    threading.Thread(target=worker1, args=(20, ), name='worker-{}'.format(2), daemon=True).start()
    print('ending')
    

七、 daemon线程和non-daemon线程

threading.Thread类中的daemon参数/属性: daemon线程和non-daemon线程

  • python程序在没有活着的non-daemon线程时退出。也就是剩下的都是daemon线程,主线程直接退出,否则只能等待non-daemon线程执行完。
  • 主线程执行完,还有daemon线程和non-daemon线程,则都继续进行。当non-daemon都执行完时,如果还有daemon,则会杀掉daemon程序退出。

可见:如果要一个线程从头到尾执行完成,一定要是一个non-daemon线程。可有可无的线程,如果主线程退出,就可以退出的线程,放到daemon线程中。

daemon属性:
线程具有一个daemon属性,可以设置为True或False,也可以不设置,则取默认值None。daemon属性必须在start()之前设置。

主线程一定是daemon=False,也就是主线程是non-daemon线程。子线程如果不设置daemon属性,就取当前线程的daemon来设置它。

从主线程创建的所有线程都不设置daemon属性时,则默认都是daemon=False,也就是non-daemon线程。

强制切换线程:time.sleep()。

这里的daemon不是linux中的守护进程
父线程:如果线程A启动了一个线程B,A就是B的父线程
子线程:B就是A的子线程

八、threading.Thread类中的join方法:

join(timeout=None),是线程的标准方法之一

  1. 一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用线程终止。
  2. 一个线程可以被join多次。
  3. timeout参数指定调用者等待多久,没有设置超时,就一直等到被调用线程结束。
  4. 调用谁的join方法,就是join谁,就是要等谁。

上述当没有活着的non-daemon线程时,主线程不等daemon线程,程序直接退出了。想要等daemon线程执行完后再退出,则可以用join方法。

总结:daemon线程应用场景
daemon唯一的作用就是:当你把一个线程设置为daemon时,它会随主线程的退出而退出。简化了程序员手动关闭线程的工作。

主要应用场景:

  • 后台任务。如发送心跳包、监控。
  • 主线程工作才有用的线程。如主线程中维护的公共资源,主线程清掉后,准备退出,工作线程使用这些资源工作也没有意义了,一起退出合适。
  • 随时可以被终止的线程。

1、如果在non-daemon线程A中,对另外一个线程B使用了join方法,则线程B设置成daemon就没什么意义了。因为线程A总是要等线程B完成后才退出;
2、如果两个都是daemon线程,即使使用了join方法,主线程退出,这两个daemon现在不管结束与否,都要退出

九、threading.local类:

解决前面的疑问,多线程使用全局变量,怎么做到线程隔离、线程安全
python提供threading.local类,将这个类实例化得到一个全局对象,可以实现不同线程使用这个对象存储的数据其他线程看不见。
原理:threading.local类构件了一个大字典,其元素是每一个线程实例的地址为key,和线程对象引用的线程单独的字典的映射:
{id(Thread): (ref(Thread), thread-local dict)}
通过threading.local实例,就可以在不同线程中,安全的使用线程独有的数据,做到了线程间数据隔离,如本地变量一样安全。

十、threading.Timer类:定时器/延时执行

Timer继承自Thread,这个类定义多久执行一个函数
class threading.Timer(interval, function,args=None, kwargs=None)
start方法执行之后,Timer对象会等待interval间隔再执行function函数。如果在等待执行函数之前,使用了cancel方法,则跳过函数结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个两个四个三

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值