1.多线程间的通信
https://www.cnblogs.com/shenh/p/10825656.html
用 threading 模块
进程:进程是操作系统资源分配的基本单位。
线程:线程是任务调度和执行的基本单位。
一个应用程序至少一个进程,一个进程至少一个线程。
两者区别:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
threading.Lock()
threading.Rlock()
threading.Condition() 可以理解为更加高级的锁,比 Lock 和 Rlock 的用法更高级,能处理一些复杂的线程同步问题。
threading.Event() 原理是在线程中立了一个 Flag ,默认值是 False ,当一个或多个线程遇到 event.wait() 方法时阻塞,直到 Flag 值 变为 True 。threading.Event() 通常用来实现线程之间的通信,使一个线程等待其他线程的通知 ,把 Event 传递到线程对象中。
threading.active_count():返回当前存活的线程对象的数量
threading.current_thread():返回当前线程对象
threading.enumerate():返回当前所有线程对象的列表
threading.get_ident():返回线程pid
threading.main_thread():返回主线程对象
https://www.jianshu.com/p/dbc3f3ceb5a7
GIL(Global Interpreter Lock),全局解释器锁
GIL是一个互斥锁,它防止多个线程同时执行Python字节码。这个锁是必要的,主要是因为CPython(python解释器)的内存管理不是线程安全的。
Python内部对变量或数据对象使用了引用计数器,我们通过计算引用个数,当个数为0时,变量或者数据对象就被自动释放。
这个引用计数器需要保护,当多个线程同时修改这个值时,可能会导致内存泄漏;SO,我们使用锁来解决这个问题,可有时会添加多个锁来解决,这就会导致另个问题,死锁;为了避免内存泄漏和死锁问题,CPython使用了单锁,即全局解释器锁(GIL),即执行Python字节码都需要获取GIL,这可以防止死锁,但它有效地使任何受CPU限制的Python程序都是单线程.
如何处理Python中的GIL
计算密集型程序:
高度使用CPU的程序,例如: 进行数学计算,矩阵运算,搜索,图像处理等)
使用多进程(什么是多进程呢,后续道来)
使用其它语言(将计算密集程序放到其它语言中执行)
替换解释器(可以自己尝试)
等大神解决GIL
I/O密集型程序:
I/0(Input/Output)程序是进行数据传输,例如: 文件操作,数据库,网络数据等)
使用多线程
使用多进程
使用多进程+多线程
GIL对I/O绑定多线程程序的性能影响不大,因为线程在等待I/O时共享锁.
GIL对计算型绑定多线程程序有影响,例如: 使用线程处理部分图像的程序,不仅会因锁定而成为单线程,而且还会看到执行时间的增加,这种增加是由锁的获取和释放开销的结果.
2.多进程的通信
IPC(Inter-Process Communication,进程间通信)
https://blog.csdn.net/tyhj_sf/article/details/97401263
python因为GIL的存在,其多线程只能在一个CPU中调度,对于计算密集型任务完全不能充分利用多核资源,所以需要Python多进程编程。
进程是操作系统分配和调度系统资源(CPU、内存)的基本单位。进程之间是相互独立的,每启动一个新的进程相当于把数据进行了一次克隆,子进程里的数据修改无法影响到主进程中的数据,不同子进程之间的数据也不能直接共享,这是多进程在使用中与多线程最明显的区别。
(1)信号量( semaphore ) : 信号量是一个共享资源访问者的计数器,可以用来控制多个进程对共享资源的并发访问数。它常作为一种锁机制。from threading import Semaphore
(2)信号 ( signal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。信号处理程序总是在 Python 主线程中执行,即使信号是在另一个线程中接收的。所以信号不能用作线程间通信的手段。import signal,os
signal常用的2个函数
os.kill(pid,sig)
用于从一个进程中发送一个信号给某个进程。
signal.alarm(sec)
设置时钟信号,在一定时间后给自身发送一个SIGALRM信号。非阻塞函数,sec为定时长度。python异步处理方案。
(3)管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。import os,用os.pipe()创建管道、os.fork()创建子进程、os.read()读、os.write()写、os.close()关闭进程、os.wait()和os.waitpid()是用来控制父进程管理等待子进程
(4)有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。os.mkfifo(fifo_path)创建有名管道、os.unlink(fifo_path)移除有名管道
有名管道会将自己注册到文件系统里一个文件,参数通信的进程通过读写这个文件进行通信。
fifo要求读写双方必须同时打开才可以继续进行读写操作,否则打开操作会堵塞直到对方也打开。
(5)消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。通过pip安装posix_ipc或sysv_ipc
(6)共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
共享内存也是非常高效的多进程通信方式,操作系统负责将同一份物理地址的内存映射到多个进程的不同的虚拟地址空间中。进而每个进程都可以操作这份内存。考虑到物理内存的唯一性,它属于临界区资源(临界资源是一次仅允许一个进程使用的共享资源),需要在进程访问时搞好并发控制,比如使用信号量。我们通过一个信号量来控制所有子进程的顺序读写共享内存。
通过pip install pyarrow直接安装,不需要预先定义存储空间且任意可序列化的对象均可存入共享内存,注意:pyarrow反序列化的对象为只读对象不可修改其值,想要修改对象可先通过对象copy
(7)套接字( socket ) : socket也是一种进程间通信机制,与其他通信机制不同的是,它主要用于不同机器间的进程通信,同一机器内的进程通信采用此方式是有些浪费的。
(8) 文件:使用文件进行通信是最简单的一种通信方式,一个进程将结果输出到临时文件,另一个进程从文件中读出来。
开发中的应用总结:
(1)仅进程同步不涉及数据传输,可以使用信号、信号量;
(2)若进程间需要传递少量数据,可以使用管道、有名管道、消息队列;
(3)若进程间需要传递大量数据,最佳方式是使用共享内存,推荐使用pyarrow,这样减少数据拷贝、传输的时间内存代价;
(4)跨主机的进程间通信(RPC)可以使用socket通信。
3.socket通信**
https://blog.51cto.com/xpleaf/1700032
socket也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄
socket起源于Unix,而Unix/Linux基本哲学之一就是:一切皆文件,即都可以用“打开open—>读写write/read—>关闭close”模式来操作。
socket即是一种特殊的文件,一些socket函数就是对其进行的操作:读read()/写write()、打开connect()、关闭close()。
(1)Socket服务器编程主要包括下面的几步:
1.打开socket
2.绑定到一个地址和端口
3.侦听进来的连接
4.接受连接
5.读写数据
(2)Socket客户端编程主要包括下面的几步:
1.打开socket
2.连接到一个地址和端口
3.读写数据
常用的是第一种和第二种,即for TCP和for UDP的类型,当然socket.SOCK_RAW也需要注意,因为它可以构造IP头,因此沿着这个思路,可以合伪造不同源IP地址的数据包,以对一些中小型企业服务器发动Dos攻击。
4.类怎么实现装饰器,类装饰器怎么做装饰
https://www.jb51.net/article/153188.htm //用函数装饰器装饰类
”装饰器装饰类“类似于“装饰器装饰函数”,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例(单例模式)。
https://www.jianshu.com/p/f758ef3b41d0//用类装饰器装饰函数
将类作为装饰器时需要保证以下几点
(1)类的实例是可调用的
(2)类需要一个地方讲被装饰的函数传入到类的实例里
第一条可以通过__call__实现,第二条可以通过__init__实现。
根据约定使用装饰器不能改变函数的__name__属性,因此我们还需要改进将类作为装饰器的使用方法。
https://www.cnblogs.com/ydf0509/p/9353714.html //用类装饰器装饰函数
装饰器是在不改变源代码,和不改变源函数的调用方式的前提下,给函数增加功能
python内置callable() 函数用于检查一个对象是否是可调用的。如果返回 True,object 仍然可能调用失败;但如果返回 False,调用对象 object 绝对不会成功。
对于函数、方法、lambda 函式、 类以及实现了__call__ 方法的类实例, 它都返回 True。
普通就是一个函数作为装饰器,也可以用类名作为装饰器。
因为类和函数都是callable的,都可以使用括号来调用运行他。
类装饰器和函数装饰器一样使用语法糖@,在被装饰的函数或类之前加一行@类装饰器。
类装饰器好处是相比于函数装饰器少了闭包,缺点是函数行为变了,但函数签名没有完全变过来,这样运行后函数成了装饰器类的一个实例了。
5.序列化**
https://www.cnblogs.com/wangchunli-blogs/p/9949671.html
pickle模块和json模块
erialization系列化,将内存中对象存储下来,把他变成一个个字节。
deSerialization反序列化,将文件的一个个字节到内存中。
序列化保存到文件就是持久化,可将数据序列化后持久化,或者网络传输;也可以将文件中或者网络接受到的字节序列反序列化。
pinkle库:
dumps对象序列化为bytes对象;
dump对象序列化到文件对象,就是存入到文件。
loads从bytes对象反序列化;
load对象反序列化,从文件读取数据。
json库:
比如web函数返回值为含中文的非str类型时,用robot框架调用关键字不能正常显示,用json.dumps编码后显示正常。一般测试也会这样,因为编码后返回值都是str类型,所以如果中文本身就是str类型则可以正常显示。
return json.dumps(device_dict,ensure_ascii=False)
python支持少量内建数据类型到json类型的转换。
6.反射机制
https://www.cnblogs.com/kongk/p/8645202.html
反射:通过字符串映射或修改程序运行时的状态、属性、方法
python反射机制的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!
getattr,hasattr,setattr,delattr对模块的修改都在内存中进行,并不会影响文件中真实内容。
(1)hasattr:判断对象中是否有这个方法或变量,返回True或False
(2)getattr:获取对象中的方法或变量的内存地址,比如:
f = getattr(p,“talk”) # 获取talk方法的内存地址
f() # 调用talk方法
(3)setattr:为对象添加变量或方法
setattr(p,“talk”,abc) # 将abc函数添加到对象中p中,并命名为talk
p.talk§ # 调用talk方法,因为这是额外添加的方法,需手动传入对象
(4)delattr:删除对象中的变量。注意:不能用于删除方法
动态导入模块:python提供了一个特殊的方法__import__(字符串参数)。通过它,我们就可以实现类似的反射功能__import__()方法会根据参数,动态的导入同名的模块。__import__默认只会导入最开头的圆点左边的目
def run():
inp = input(“请输入您想访问页面的url: “).strip()
modules, func = inp.split(”/”)
obj =__import_(modules) //返回模块
obj = __import_(“lib.” + modules, fromlist=True) # 注意fromlist参数,在另一个目录下则需要添加fromlist=True



