Python多线程记录

最近需要用到多线程相关的内容,因此这里记录下

1、多线程的意义及理解

我们一般而言肯定是不希望我们强大的电脑一段时间只做一件事情的,这样未免太浪费资源了,正如我们在使用单片机的时候,如果单片机需要处理的事情比较多的时候我们都会给他建立操作系统(例如FreeRtos,RT-thread)这样的来执行,这样可以方便我们同时处理多件事情,效率也提高了!

因此这里多线程就比较重要了,这里我们先来看下我们直观的去看怎么去实现,比如我们需要两个任务,我们把任务放到while里面,比较直白的:
在这里插入图片描述
我们运行下,运行结果如下,他先结果并没有按照我们想的那样执行程序,任务执行有问题,这是先把一个任务执行完了,在执行另外的一个任务,当然这个原因只要仔细看看代码就能知道,但这显然不是我们想要看到的对吧;
在这里插入图片描述
因此我们其实就需要一个多任务(这里和多线程是一个意思)的东西来使它符合我们想要的了,不过我们电脑再强它其实也只有一个CPU,所以实际上的多任务本事上还是一个任务,其实是系统在做完这个任务的一部分后立马跳到一个任务,至于具体跳到哪个任务,任务执行多久,那就是系统的事情了,系统来执行任务调度。不过这里切换任务的速度非常快,所以基本上看不出来任务是一个个执行的。

2、python使用多线程

python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用

下面我们先来执行一个单一的线程试试看:

这里使用time的延时函数就可以实现延时执行了:
在这里插入图片描述
运行程序可以看到打印了五次结果:
在这里插入图片描述
下面可以开始进行一个多线程的测试来看看:

这里引入threading模块来创建线程,用的还是我们一开始的例子,创建了两个任务
在这里插入图片描述
运行后可以看到效果
在这里插入图片描述
下面我们来看下线程是如何运行的,我们在创建线程之后加上一段代码,如下所示
在这里插入图片描述
运行之后的结果如下所示,这样我们其实就可以知道,创建后运行一下就会执行一次后面的内容,不过马上又进入线程,直到线程都运行结束为止。
在这里插入图片描述
当然也提供了线程查询函数,用法如下:
在这里插入图片描述
运行效果如下所示
在这里插入图片描述

3、多线程使用过程中的全局变量共享问题

1、全局变量使用及问题

我们知道,python中有全局变量的概念,在函数中对全局变量进行修改,会影响全局变量的最终结果,下面看下在不同线程中使用全局变量的效果:

这里我们创建两个任务来进行测试看看
在这里插入图片描述
打印全局变量效果
在这里插入图片描述

因此在一个全局变量进行全局变量可以:

  • 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
  • 线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

那么使用多线程会对我们这个这系统造成什么影响呢,主要还是多线程造成的混用问题吧:

这里我贴出程序吧,这样方便进行测试:

import threading

g_num = 0

def work1(num):
    global g_num
    for i in range(num):
        g_num += 1
    print("----in work1, g_num is %d---"%g_num)

def work2(num):
    global g_num
    for i in range(num):
        g_num += 1
    print("----in work2, g_num is %d---"%g_num)

print("---线程创建之前g_num is %d---"%g_num)

t1 = threading.Thread(target=work1, args=(100,))
t1.start()
t2 = threading.Thread(target=work2, args=(100,))
t2.start()

print("2个线程对同一个全局变量操作之后的最终结果是:%s" % g_num)

运行结果如下所示:
在这里插入图片描述
这里两个任务用的都是一个全局变量,都是加了100次,但是最后只有线程2的数值是200,任务1只有100,这里我们来看下这个过程是怎么实现的:

  • 在g_num=0时,t1取得g_num=0。此时系统把t1调度为”sleeping”状态,把t2转换为”running”状态,t2也获得g_num=0
  • t2对得到的值进行加1并赋给g_num,使得g_num=1
  • 系统又把t2调度为”sleeping”,把t1转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。
  • 这样导致虽然t1和t2都对g_num加1,但结果仍然是g_num=1

所以从这里就可一看出,其实在使用全局变量的时候任务之间会进行相互的竞争,然后导致全局变量的数值不准确。

2、使用同步的方法来解决问题

同步就是协同步调,按预定的先后次序进行运行

在线程的体现就比如:线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B执行,再将结果给A;A再继续操作,这个思路对于上面的问题解决方案如下:

  • 系统调用t1,然后获取到g_num的值为0,此时上一把锁,即不允许其他线程操作g_num
  • t1对g_num的值进行+1
  • t1解锁,此时g_num的值为1,其他的线程就可以使用g_num了,而且是g_num的值不是0而是1
  • 同理其他线程在对g_num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,就保证了数据的正确性

代码如下:

import threading
import time

g_num = 0

def test1(num):
    global g_num
    for i in range(num):
        mutex.acquire()  # 上锁
        g_num += 1
        time.sleep(0.05)
        mutex.release()  # 解锁
    print("---test1---g_num=%d"%g_num)

def test2(num):
    global g_num
    for i in range(num):
        mutex.acquire()  # 上锁
        g_num += 1
        time.sleep(0.05)
        mutex.release()  # 解锁
    print("---test2---g_num=%d"%g_num)

mutex = threading.Lock()
p1 = threading.Thread(target=test1, args=(10,))
p1.start()
p2 = threading.Thread(target=test2, args=(10,))
p2.start()

while len(threading.enumerate()) != 1:
    time.sleep(1)
print("2个线程对同一个全局变量操作之后的最终结果是:%s" % g_num)

运行程序,可以看到已经改善很多了
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python 中,可以使用 `logging` 模块记录日志。如果需要在多线程环境下记录日志,可以使用 `concurrent_log_handler` 模块来实现。 首先需要安装 `concurrent_log_handler` 模块: ``` pip install concurrent_log_handler ``` 然后,可以使用以下代码来记录日志: ```python import logging from concurrent_log_handler import ConcurrentRotatingFileHandler import threading # 创建日志记录器 logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 创建文件处理器 handler = ConcurrentRotatingFileHandler(filename='example.log', mode='a', maxBytes=1024, backupCount=3) handler.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) # 将处理器添加到记录器 logger.addHandler(handler) # 多线程记录日志 def worker(): logger.debug('Debug message from thread {0}'.format(threading.current_thread().name)) logger.info('Info message from thread {0}'.format(threading.current_thread().name)) logger.warning('Warning message from thread {0}'.format(threading.current_thread().name)) logger.error('Error message from thread {0}'.format(threading.current_thread().name)) logger.critical('Critical message from thread {0}'.format(threading.current_thread().name)) threads = [] for i in range(10): t = threading.Thread(target=worker) threads.append(t) t.start() for t in threads: t.join() ``` 上面的代码中,创建了一个 `ConcurrentRotatingFileHandler` 类实例化的文件处理器,它可以在多线程环境下安全地写入日志文件。然后,将处理器添加到日志记录器中,并使用多线程记录日志。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

桃成蹊2.0

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

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

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

打赏作者

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

抵扣说明:

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

余额充值