Python多线程编程,多线程定时器,文件锁,文件读取权限的知识记录

Python多线程编程,多线程定时器,文件锁,文件读取权限的知识记录

 

最近有个需求:统计交换机最近的接口收发速率和的信息,要带时间戳

Web发出HTTP请求,得到对应json信息返回过去,然后绘图

 

这里根据C-S客户端服务器模型,我需要在服务器端不断地更新数据,写到文件里,供客户端读文件信息

为了减少系统开销,我在REST服务器主线程端开启了一个子线程 

这个子线程的功能是:轮询更新需要的信息,每分钟更新一次,把信息存到文件里

需要用Timer函数重写线程方法

class RepeatingTimer(Timer):
    """用定时器方法重写线程类"""
    def run(self):
        while not self.finished.is_set():
            self.function(*self.args, **self.kwargs)
            self.finished.wait(self.interval)

主线程和子线程之间可以通信,也就是传参

t1 = RepeatingTimer(2.99,thread1,args=(Queue_data,))
    t1.start()

如代码块所示,根据重写的Timer方法可以设定多少s执行一次这个线程,args= 即是主线程传递给此方法的参数

(当然也可以用共享变量,即全局变量的方式实现)

复习一下操作系统知识

几种进程间的通信方式

(1) 管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有血缘关系的进程间使用。进程的血缘关系通常指父子进程关系。

(2)有名管道(named pipe):有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间通信。

(3)信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

(4)消息队列(message queue):消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

(5)信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某一事件已经发生。

(6)共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问,共享内存是最快的IPC方式,它是针对其他进程间的通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。

(7)套接字(socket):套接口也是一种进程间的通信机制,与其他通信机制不同的是它可以用于不同及其间的进程通信。

几种线程间的通信机制

1、锁机制

     1.1 互斥锁:提供了以排它方式阻止数据结构被并发修改的方法。

     1.2 读写锁:允许多个线程同时读共享数据,而对写操作互斥。

     1.3 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

2、信号量机制:包括无名线程信号量与有名线程信号量

3、信号机制:类似于进程间的信号处理。

线程间通信的主要目的是用于线程同步,所以线程没有象进程通信中用于数据交换的通信机制。

# Python中一个线程对应于C语言中的一个线程(CPython而言)(Python并不一定就慢,视情况而定) #pypy解释器专门克服gil慢的一种解释器(去gil化) #GIL使用同一个时刻只有一个线程在一个cpu上执行字节码,无法将多个线程映射到多个CPU上 #gil锁会根据执行的字节码或时间片划分适当的释放(python内部实现机制) #该实例来说明GIL在某种情况下会自动释放让下一个线程去执行(时间片来回切换) #反编译(函数执行流程)同一时刻只有一个线程在CPU上执行

对于python,多线程编程间常用的线程间通信方式如下

1.线程间的通信方式–共享变量

2.线程间的通信方式–通过Queue模块进行线程间同步

3:线程上下文

4:共享内存

5:套接字(Socket),不同的机器之间进行通信

为了更好的利用多核CPU,引入了多线程的概念

为了保护多线程中数据的完整性和线程间状态的同步,一个简单的办法就是加锁

全局解释器锁(GIL)

全局解释器锁(Global Interpreter Lock,GIL)是一个加在解释器上的锁,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用CPU

在同一个进程中只要有一个线程获取了全局解释器(CPU)的使用权限,那么其他的线程就必须等待该线程的全局解释器(CPU)使用权消失后才能使用全局解释器(CPU)

由于GIL(可以理解为全局排他锁)的存在,Python的多线程实际上是一种假象

当然可以选择不使用GIL,这和Python的解释器时有关的,CPython,PyPy,Psyco,都是Python的解释器,其中CPython用的比较广泛,而它是采用GIL的——这样的依赖关系在一方面导致了Python性能的低效

GIL总结起来两点,它是重量级的而且和解释器相关(也许会降低Python的性能,让Python多线程变成假的多线程,实际上的顺序单线程)

 

同步锁(Synchrolock)

同一时刻的一个进程下的一个线程只能使用一个CPU,要确保这个线程下的程序在一段时间内被CPU执行,就要用到同步锁(synchrolock)

特别的在文件IO中我们需要同步锁,比如在执行某个线程下的文件IO到一半可能会切换到另外的线程上去,这就对文件数据的完整性造成了威胁,这就需要同步锁的帮忙

使用同步锁也很简单,只需要在对公共数据的操作前后添加上锁和释放锁的操作即可

 

如图代码所示

关于join的用法:

首先需要明确几个概念:

知识点一:
当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时,主线程会创建多个子线程,在python中,默认情况下(其实就是setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束,例子见下面一。

知识点二:
当我们使用setDaemon(True)方法,设置子线程为守护线程时,主线程一旦执行结束,则全部线程全部被终止执行,可能出现的情况就是,子线程的任务还没有完全执行结束,就被迫停止,例子见下面二。

知识点三:
此时join的作用就凸显出来了,join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止,例子见下面三。

知识点四:
join有一个timeout参数:

  1. 当设置守护线程时,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序。所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和。简单的来说,就是给每个子线程一个timeout的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。
  2. 没有设置守护线程时,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,子线程依然可以继续执行,直到子线程全部结束,程序退出。

有了join的存在,主线程一直等待全部的子线程结束之后,主线程自身才结束,程序退出。

 

利用这个机制,如果我们有两个线程,都需要对同一文件资源进行操作的时候 怎么办呢?

这里复习一下python中对文件操作权限的知识:

只要 with open ("xxx.json","w") as f:

这种模式带w的权限,就会在with open后清空打开的文件然后重写

如果你想只是每次加入一点东西的情况怎么办呢?

那就用r+的权限,写内容进文件的时候不清空只加入

 

考虑到读和写两个线程可能同时存在的情况,我们引入文件锁机制

import fcntl

with open ("stats_trends.json","r") as f:
                fcntl.flock(f.fileno(), fcntl.LOCK_EX) #加锁
                str_all_speed_data=f.read()

即在with结束后锁结束,防止冲突的情况

 

对于读json的内容时,返回的是字符串形式

比如

a='[1,2,3]'

如何把a变成list然后拿来直接用呢

from ast import literal_eval

data=literal_eval(str_data)

如代码块所示即可解决问题

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值