当前位置: 首页 > news >正文

python 进程笔记二(通讯) (概念+示例代码)

1、为什么要掌握进程间通信

        Python代码效率由于受制于GIL全局锁限制,多线程不能利用多核CPU来加速,而多进程方式却可以绕过GIL限制, 发挥多CPU加速的优势,达到提高程序的性能的目的。

        然而进程间通信却是不得不考虑的问题。 进程不同于线程,进程有自己的独立内存空间,不能使用全局变量在进程间传递数据。

        实际项目需求中,常常存在密集计算、或实时性任务,进程之间有时需要传递大量数据,如图片、大对象等,传递数据如果通过文件序列化、或网络接口来进行,难以满足实时性要求,采用redis,或者kaffka, rabbitMQ 之第3方消息队列包,又使系统复杂化了,而且多次重复编码解码数据,效率并不高。

        Python multiprocessing 模块本身就提供了丰富高效的进程间通信机制,包括消息机制、同步机制、共享内存等。

        了解并掌握 python 各种进程间通信的特点、使用方式,以及安全机制,可以帮助软件工程师在实际项目选用最合适的通信方式,从而获得程序运行的最佳性能。

2、进程间各类通信方式简介

下面列出了python支持的各种进程间通信方式

各类通信方式的适用场景
主要是由于不同应用场景,以及对性能的不同需求,使用通信方式。

  • 多进程同时访问数据库,读写文件等场景,需要添加同步机制,可采用Lock, Event, Semaphore等机制。
  • 而Queue队列、Pipe 适合大多数场景,如 生产者 – 消息者,订阅 – 发布,Director – Worker等场景,在大多数情况下,Queue, Pipe即可满足进程间通信的需求。
  • 由于Queue对入列、出列对象需要进行序列化操作等,影响了效率。如果对性能要求高,可采用共享变量方式交换数据。
  • 如果希望python性能飞起来,共享内存方式是不二之选,非常适合进程之间交换图片、视频等大块数据,只是对开发者要求更高,必须熟悉bytes, bytearray, memoryview类型的使用,以及pickle, json序列化、字符串编解码等知识。

关于进程间通信的内存安全
        同1个应用程序的多进程间通信,可能会因同抢,意外销毁等原因造成共享变量异常,或者使用不当造成内存占用过高,也可能被其它进程非授权访问。
        Multiprocessing 模块提供的Queue, Pipe, Lock, Event 对象,都已实现了进程间通信安全机制。
        需要注意,采用共享内存方式通信,需要在代码中开发者自己来跟踪、销毁这些共享内存变量。除非开发者很清楚共享内存使用特点,否则不建议直接使用此共享内存,而是通过Manager管理器来使用共享内存。

进程管理器Manager
        Multiprocessing提供了管理器Manager类,SharedMemoryManager类,可解决进程通信的内存安全问题,可以将各种共享数据加入管理器,包括 list, dict, Queue, Lock, Event, SharedMemory 等,由其统一跟踪与销毁。

下面按类别介绍种通信方式的特点以及代码开发步骤。

3、消息机制通信

1) 管道 Pipe 通信方式

类似于1上简单的socket通道,双端均可收发消息。
Pipe 对象的构建方法:

parent_conn, child_conn = Pipe(duplex=True/False) 

参数说明

  • duplex=True, 管道为双向通信
  • duplex=False, 管道为单向通信,只有child_conn可以发消息,parent_conn只能接收。

示例代码:

from multiprocessing import Process, Pipe
def myfunction(conn):conn.send(['hi!! I am Python'])conn.close()if __name__ == '__main__':parent_conn, child_conn = Pipe()p = Process(target=myfunction, args=(child_conn,))p.start()print (parent_conn.recv() )p.join()#结果
#['hi!! I am Python']

2) 消息队列Queue通信方式

Multiprocessing 的Queue 类,是在python queue 3.0版本上修改的, 可以很容易实现生产者 – 消息者间传递数据,而且Multiprocessing的Queue 模块实现了lock安全机制。

Queue模块共提供了3种类型的队列。

(1) FIFO queue , 先进先出,

class queue.Queue(maxsize=0)

(2) LIFO queue, 后进先出, 实际上就是堆栈

class queue.LifoQueue(maxsize=0)

(3) 带优先级队列, 优先级最低entry value lowest 先了列

class queue.PriorityQueue(maxsize=0)

Multiprocessing.Queue类的主要方法:

methodDescription
queue.qsize()返回队列长度
queue.full()队列满,返回 True, 否则返回False
queue.empty()队列空,返回 True, 否则返回False
queue.put(item)将数据写入队列
queue.get()将数据抛出队列 ,
queue.put_nowait(item), queue.get_nowait()无等待写入或抛出

说明:

  • put(), get() 是阻塞方法, 而put_notwait(), get_nowait()是非阻塞方法。
  • Multiprocessing 的Queue类没有提供Task_done, join方法

Queue模块的其它队列类:

(1) SimpleQueue
简洁版的FIFO队列, 适事简单场景使用

(2) JoinableQueue子类
Python 3.5 后新增的 Queue的子类,拥有 task_done(), join() 方法

  • task_done()表示,最近读出的1个任务已经完成。
  • join()阻塞队列,直到queue中的所有任务都已完成。

producer – consumer 场景,使用Queue的示例

import multiprocessing
import random
def producer(numbers, q):for x in numbers:if x % 2 == 0:if q.full():print("queue is full")breakq.put(x)print(f"put {x} in queue by producer")return Nonedef consumer(q):while not q.empty():print(f"take data {q.get()} from queue by consumer")return Noneif __name__ == "__main__":# 设置1个queue对象,最大长度为5qu = multiprocessing.Queue(maxsize=5,)# 创建producer子进程,把queue做为其中1个参数传给它,该进程负责写p5 = multiprocessing.Process(name="producer-1",target=producer,args=([random.randint(1, 100) for i in range(0, 10)], qu))p5.start()p5.join()#创建consumer子进程,把queue做为1个参数传给它,该进程中队列中读p6 = multiprocessing.Process(name="consumer-1",target=consumer,args=(qu,))p6.start()p6.join()print(qu.qsize())#结果
# put 80 in queue by producer
# put 32 in queue by producer
# take data 80 from queue by consumer
# take data 32 from queue by consumer
# 0

4、同步机制通信

(1) 同步锁 – Lock

        Multiprocessing也提供了与threading 类似的同步锁机制,确保某个时刻只有1个子进程可以访问某个资源或执行某项任务, 以避免同抢。

        例如:多个子进程同时访问数据库表时,如果没有同步锁,用户A修改1条数据后,还未提交,此时,用户B也进行了修改,可以预见,用户A提交的将是B个修改的数据。

        添加了同步锁,可以确保同时只有1个子进程能够进行写入数据库与提交操作。

        如下面的示例,同时只有1个进程可以执行打印操作。

from multiprocessing import Process, Lockdef f(l, i):l.acquire()try:print('hello world', i)finally:l.release()if __name__ == '__main__':lock = Lock()for num in range(10):Process(target=f, args=(lock, num)).start()# 结果
# hello world 4
# hello world 0
# hello world 5
# hello world 7
# hello world 2
# hello world 3
# hello world 8
# hello world 9
# hello world 6
# hello world 1

不过,acquire() 未能获取锁的话,会阻塞当前线程,直到其它线程将锁release掉,才会继续执行。若有两个以上的锁对象,容易造成死锁,多人项目运用Lock通信时应慎重。

(2) 子进程间协调机制 – Event

Event 机制的工作原理:

1个event 对象实例管理着1个 flag标记, 可以用set()方法将其置为true, 用clear()方法将其置为false, 使用wait()将阻塞当前子进程,直至flag被置为true.
这样由1个进程通过event flag 就可以控制、协调各子进程运行。

Event 是一种进程间通信的信号机制,以异步方式运行。此对象与pytho signal模块的中断信号工作机制是不同的。

Event 对象的使用方法:

1)主进程: 创建1个event 对象,如: flag = multiprocessing.Event() , 做为参数传给各子进程
2) 子进程joo_b: 不受event影响,通过event 控制其它进程的运行

  • 先clear(),将event 置为False, 占用运行权.
  • 完成工作后,用set()把flag置为True。

3)子进程 joo_a: 受 event 影响

  • event对象flag设置为 wait() 状态,暂停运行
  • 直到flag重新变为True,恢复运行

Event对象主要方法:

  • set(), clear()设置 True/False,
  •  wait() 使进程等待,直到flag被改为true.
  • is_set(), Return True if and only if the internal flag is true.

详细代码,如下

import multiprocessing
import time
import randomdef joo_a(q, ev):print("subprocess joo_a start")if not ev.is_set():ev.wait()q.put(random.randint(1, 100))print("subprocess joo_a ended")def joo_b(q, ev):print("subprocess joo_b start")ev.clear()time.sleep(2)q.put(random.randint(200, 300))ev.set()print("subprocess joo_b ended")def main_event():qu = multiprocessing.Queue()ev = multiprocessing.Event()sub_a = multiprocessing.Process(target=joo_a, args=(qu, ev))sub_b = multiprocessing.Process(target=joo_b, args=(qu, ev,))sub_a.start()sub_b.start()# ev.set()sub_a.join()sub_b.join()while not qu.empty():print(qu.get())if __name__ == "__main__":main_event()# 结果
# subprocess joo_a start
# subprocess joo_b start
# subprocess joo_b ended
# subprocess joo_a ended
# 267
# 59

5、共享变量

进程间通信不能使用全局变量
由于每个子进程运行在自己独立内存空间,只是创建时全局变量复制过来了。因此在子进程A中修改了全局变量的值,子进程B是无法看到的。

原理: 在主进程中创建ctype共享变量对象,再被子进程继承 。
优点: 速度比queue快很多

Python相关支持模块:
• multiprocessing.Value, multiprocessing.Array. 是Sharedctypes的派生类
• multiprocessing.sharedctypes, 则支持更全面ctypes数据类型.

Value()语法:

Value(typecode_or_type, *args, lock=True)

参数说明:

  • typecode: ctypes 类型缩写,或 ctype类型名
    如’i’ 表示 c_init, ‘d’ 表示 c_double, ‘c’ 表示 c_char等。
  • *args 传入值
  • Lock lock=true, 表示添加1个锁

返回值
返回一个从共享内存上创建的 ctypes 对象
示例

camera_unit = multiprocessing.Value(ctypes.c_int,0)
servo_on_flag = multiprocessing.Value(ctypes.c_bool,False)
target_angle = multiprocessing.Value(‘d’,30.0) 

注意 使用 share memory 要考虑同抢等问题,释放等问题,需要手工实现。因此在使用共享变量时,建议使用Manager管程来管理这些共享变量。

import multiprocessing
def func(num):num.value = 10.78  # 子进程改变数值的值,主进程跟着改变if __name__ == "__main__":num = multiprocessing.Value("d", 10.0)
# d表示数值,主进程与子进程可共享这个变量。p = multiprocessing.Process(target=func, args=(num,))p.start()p.join()print(num.value)# 结果
# 10.78

进程之间共享数据(数组型):

multiprocessing.Array() 返回1个array, 元素为ctypes类型

import multiprocessingdef func(num):num[2] = 9999  # 子进程改变数组,主进程跟着改变if __name__ == "__main__":num = multiprocessing.Array("i", [1, 2, 3, 4, 5])p = multiprocessing.Process(target=func, args=(num,))p.start()p.join()print(num[:])# 结果
# [1, 2, 9999, 4, 5]

sharedctypes.RawValue() 使用方式与 Value() 是类似的,只是typecode必须是ctypes类型全称,本文不再介绍。

6、共享内存 Shared_memory

        Python 提供了一个 SharedMemory 类,用于分配和管理多核或对称多处理器(SMP)机器上进程间的共享内存,而非直接共享变量,
        共享内存方式与 ctypes共享变量是不同的。前者共享的是二进制内存,后者共享对象为ctype变量,更友好。
        ShareMemory处理对象为binary类型,因此对编程者并不友好,但优点是处理速度快。

        注意:直接使用SharedMemory 也存在着同抢、泄露隐患,应通过SharedMemory Manager 管理器来使用, 以确保内存安全。

1) SharedMemory对象编程步骤

创建共享内存区:

multiprocessing.shared_memory.SharedMemory(name=none, create=False, size=0)

参数说明:

  • name: 全局唯一的名称
  • create 指定创建一个新的共享内存块 (True) 还是连接到已存在的共享内存块(False)

使用方法:
父进程创建shared_memory 后,子进程可以使用它,当不再需要后,使用close(), 删除使用unlink()方法
相关属性:
获取内存区内容: shm.buf,为 memoryview 类型
获取内存区名称: shm.name
获取内存区字节数: shm.size

SharedMemory 编程步骤:
1) 在主进程中创建新的SharedMemory 块

shm = shared_memory.SharedMemory(name="shmArray",create=True, size=10)

shmArray为共享内存的名字,其它进程中可以使用它来attach
初始化:

shm.buf[0:5] = bytearray([10,3,8,1,5])  

2)将共享内存对象做为参数传给子进程任务函数

p1 = mp.Process(target=task_a,args=(shm,)) 

3) 子进程读取修改共享内存对象

    buff = shm.bufbuff[5] = 200   # modify the shared memoryprint(f"shared_memeory: {buff[0:9].tolist()}")

4) 使用attach 方式共享内存对象。
这种方式工,不需将共享内存对象做为参数传给子进程中。子进程通过attach方式连接到主进程已创建好的共享内存对象。
具体使用:初始化SharedMemory对象时,name为已存在共享内存对象名,create参数设置为False, 如下:

shm_b = shared_memory.SharedMemory(name='shmArray',create=False)

这样,shm_b指向了主进程中创建的shm.
5) 释放共享内存对象
子进程通过attach方式访问,使用用close()方法关闭。所有子进程都不再使用后,调用unlink()方法释放共享内存对象。

示例: 创建两个进程,使用共享内存通信. 进程p1接受传入SharedMemory 对象,进程p2使用attach 方式访问主进程创建的SharedMemory 对象。

from multiprocessing import shared_memory
import multiprocessing as mp
import osdef task_a(shm):"""Task function for multiprocessingInputs:shm: shared_memory object"""# create a shared memory object, attached to exsisting one.buff = shm.buf# modify the shared memorybuff[5] = 200print(f"current process: { os.getpid()}, shared_memeory: {buff[0:9].tolist()}")return Nonedef task_b():"""Task function for multiprocessingno input, access existing sharedMemory object."""# create shared memory object, attached to exsisting one.shm_b = shared_memory.SharedMemory(name='shmArray',create=False)buff = shm_b.bufbuff[6] = 100# Pytho SharedMemory class may have a bug:the length of attached SharedMemory object# is not equal to original one.print(f"current process: { os.getpid()}, shared_memeory: {buff[0:9].tolist()}")print(f"buff size: {shm_b.size}")shm_b.close()return Noneif  __name__=="__main__":# 创建两个进程,使用共享内存通信# create a shared memory objectshm = shared_memory.SharedMemory(name="shmArray",create=True, size=10)shm.buf[0:5] = bytearray([10,3,8,1,5])      # initialize the shared memoryp1 = mp.Process(target=task_a,args=(shm,))  # 将sharedMmemory对象作为参数传递给子进程p2 = mp.Process(target=task_b,)             # 无参数p1.start()p1.join()p2.start()p2.join()print("in main process, shared_memory: ",shm.buf.tolist() )shm.close()shm.unlink()# 结果
# current process: 17732, shared_memeory: [10, 3, 8, 1, 5, 200, 0, 0, 0]
# current process: 12356, shared_memeory: [10, 3, 8, 1, 5, 200, 100, 0, 0]
# buff size: 4096
# in main process, shared_memory:  [10, 3, 8, 1, 5, 200, 100, 0, 0, 0]

多个子进程对同1个SharedMemory对象同时修改操作,如何避免同抢?
参考第4章,子进程中在操作之前,使用 Lock同步锁来避免同抢。

2) ShareableList 共享列表
sharedMemory类还提供了1个共享列表类型,这样就更方便了,进程间可以直接共享python强大的列表
构建方法:
multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)


from multiprocessing import shared_memory
>>> a = shared_memory.ShareableList(['howdy', b'HoWdY', -273.154, 100, None, True, 42])
>>> [ type(entry) for entry in a ]
[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'NoneType'>, <class 'bool'>, <class 'int'>]
>>> a[2]
-273.154
>>> a[2] = -78.5
>>> a[2]
-78.5
>>> a[2] = 'dry ice'  # Changing data types is supported as well
>>> a[2]
'dry ice'
>>> a[2] = 'larger than previously allocated storage space'
Traceback (most recent call last):...
ValueError: exceeds available storage for existing str
>>> a[2]
'dry ice'
>>> len(a)
7
>>> a.index(42)
6
>>> a.count(b'howdy')
0
>>> a.count(b'HoWdY')
1
>>> a.shm.close()
>>> a.shm.unlink()
>>> del a  # Use of a ShareableList after call to unlink() is unsupportedb = shared_memory.ShareableList(range(5))         # In a first process
>>> c = shared_memory.ShareableList(name=b.shm.name)  # In a second process
>>> c
ShareableList([0, 1, 2, 3, 4], name='...')
>>> c[-1] = -999
>>> b[-1]
-999
>>> b.shm.close()
>>> c.shm.close()
>>> c.shm.unlink()

7、管理器Manager

Multiprocessing 提供了 Manager 管理器类,当调用1个Manager实例对象的start()方法时,会创建1个manager进程,其唯一目的就是管理共享变量、共享内存, 避免出现进程间共享数据不同步,内存泄露等现象。

其原理如下:

Manager管理器相当于提供了进间程共享内存对象的服务,不仅可以被主进程创建的多个子进程使用,还可以被其它进程访问,甚至跨网络访问。本文仅聚焦于由单一主进程创建的各子进程之间的通信。

1) Manager的主要数据结构

相关类:multiprocessing.Manager
还有两个子类:

  • multiprocessing.managers.SharedMemoryManager
  • multiprocessing.managers.BaseManager

支持共享变量类型:

Manger类支持的共享变量类型有: dic, list,Queue, Lock, Event, Condition, Semaphore, Barrier等类型
通过代理可支持:python各种类型, 以及ctypes 类型
SharedMemoryManager子类,额外支持 SharedMemory,ShareableList类型。

2) 使用步骤

1)创建管理器对象
snm = Manager() 或者
snm = SharedMemoryManager()

2)创建共享内存变量
新建list, dict
sl = snm.list()
sd = snm.dict()

新建1块bytes共享内存变量,需要指定大小
sx = snm.SharedMemory(size)

新建1个共享列表变量,可用列表来初始化
sl = snm.ShareableList(sequence) 如
sl = smm.ShareableList([‘howdy’, b’HoWdY’, -273.154, 100, True])

新建1个queue, 使用multiprocessing 的Queue类型
q = snm.Queue()

传入子进程

def foo(s, d):.....p1 = Process( target=foo, args=(sl, sd, ) )
p1.start()
p1.join()

示例 :

from multiprocessing import Process, Managerdef f(d, l):d[1] = '1'd['2'] = 2d[0.25] = Nonel.reverse()if __name__ == '__main__':with Manager() as manager:d = manager.dict()l = manager.list(range(10))p = Process(target=f, args=(d, l))p.start()p.join()print(d)print(l)

将打印

{0.25: None, 1: '1', '2': 2}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

3) 销毁共享内存变量

方法一:
调用snm.shutdown()方法,会自动调用每个内存块的unlink()方法释放内存。或者 snm.close()
方法二
使用with语句,结束后会自动释放所有manager变量

>>> with SharedMemoryManager() as smm:
...     sl = smm.ShareableList(range(2000))
...     # Divide the work among two processes, storing partial results in sl
...     p1 = Process(target=do_work, args=(sl, 0, 1000))
...     p2 = Process(target=do_work, args=(sl, 1000, 2000))
...     p1.start()
...     p2.start()  # A multiprocessing.Pool might be more efficient
...     p1.join()
...     p2.join()   # Wait for all work to complete in both processes
...     total_result = sum(sl)  # Consolidate the partial results now in sl

4) 向管理器注册自定义类型

managers的子类BaseManager提供register()方法,支持注册自定义数据类型。如下例,注册1个自定义MathsClass类,并生成实例。

from multiprocessing.managers import BaseManagerclass MathsClass:def add(self, x, y):return x + ydef mul(self, x, y):return x * yclass MyManager(BaseManager):passMyManager.register('Maths', MathsClass)if __name__ == '__main__':with MyManager() as manager:maths = manager.Maths()print(maths.add(4, 3))         # prints 7print(maths.mul(7, 8))  

7、总结

Python多进程(multiprocessing) 编程是绕开GIL提升程序性能的重要方式,进程间通信方式包括消息机制(pipe, queue)、同步机制( Lock, Event) 、Shared Memory(Value, Array, Shared_Memory, etc)等。
直接使用Shared Memory共享内存是不安全的,Multiprocessing.Manager模块提供了安全管理共享内存变量的管理器功能。
在实际编程时,根据主进程与子进程,子进程之间所要交换数据的类型、大小,频度、实时性等需求,来选择适合的通信方式。

8、进程、线程对比

8.1功能

进程:能够完成多任务,比如在一条电脑上能够同时运行多个QQ

线程:能够完成多任务,比如一个QQ中多个聊天窗口

定义的不同:线程是系统进行资源分配和调度的一个独立单位

线程是进程的一个实体,是CPU调度和分配的基本单位,它是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是它可与同一个进程的其他线程共享进程所拥有的全部资源

8.2 区别

一个程序至少有一个进程,一个进程至少有一个线程

线程的划分尺度小于进程(资源比进程少)使得多线程程序的并发性高

进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率

线程不能独立执行,必须依存在进程中

可以将进程理解为工厂中的一条流水线,而其中的线程就是流水线中的工人

8.3 优缺点

线程:线程执行开销小,但不利于资源的管理和保护

进程:进程执行开销大,但利于资源的管理和保护

参考:

1.由浅入深掌握各种 Python 进程间通信方式(建议收藏)

2.一文读懂Python进程间通信的几种方式

相关文章:

python 进程笔记二(通讯) (概念+示例代码)

1、为什么要掌握进程间通信 Python代码效率由于受制于GIL全局锁限制&#xff0c;多线程不能利用多核CPU来加速&#xff0c;而多进程方式却可以绕过GIL限制, 发挥多CPU加速的优势&#xff0c;达到提高程序的性能的目的。 然而进程间通信却是不得不考虑的问题。 进程不同于线程&a…...

电机控制-----电机极对数,相电感,相电阻以及磁链常数的测量

电机控制-----电机极对数&#xff0c;相电感&#xff0c;相电阻以及磁链常数的测量 我们在做电机控制的时候&#xff0c;拿到一个电机首先要知道它的参数&#xff0c;然后才能进行相应的开发&#xff0c;我这里介绍的是通过平常常用的手段去获得电机的参数&#xff1a;极对数&…...

SQL注入之oracle注入+SQLbypass+sqlmap实战

学习路还很长&#xff0c;切莫轻言放弃&#xff01; 目录 Oracle数据库介绍 Oracle数据库和MySQL数据库的差别 Oracle数据库注入 SQLbypass姿势 sqlmap工具实战(kali自带) Oracle数据库介绍 Oracle数据库是全球最知名的关系型数据库管理系统&#xff08;RDBMS&#xff09…...

【GPTs分享】GPTs分享之Write For Me

Write For Me 是一个专门定制的GPT版本&#xff0c;旨在为用户提供高质量的文本内容创作服务。它适用于各种写作需求&#xff0c;从商业计划、学术文章到创意故事等。下面是从简介、主要功能、使用案例、优点和局限性几个方面对Write For Me 的详细介绍。 简介 Write For Me …...

css4浮动+清除浮动

浮动 一.常见网页布局1.三种布局方式2.布局准则 二.浮动&#xff08;float&#xff09;1.好处2.概念3.三大特性4.使用5.常见网页布局模板6.注意点 三.清除浮动1.why2.本质3.语法4.四种way&#xff08;后三个都是给父级添加&#xff09;清除浮动总结 一.常见网页布局 1.三种布局…...

外包干了3个月,技术倒退明显...

先说情况&#xff0c;大专毕业&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近6年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…...

STM32控制数码管从0显示到99

首先 先画电路图吧&#xff01;打开proteus&#xff0c;导入相关器件&#xff0c;绘制电路图。如下&#xff1a;&#xff08;记得要保存啊&#xff01;发现模拟一遍程序就自动退出了&#xff0c;有bug&#xff0c;我是解决不了&#xff0c;所以就是要及时保存&#xff0c;自己重…...

【机器学习算法】KNN鸢尾花种类预测案例和特征预处理。全md文档笔记(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论机器学习算法相关知识。机器学习算法文章笔记以算法、案例为驱动的学习&#xff0c;伴随浅显易懂的数学知识&#xff0c;让大家掌握机器学习常见算法原理&#xff0c;应用Scikit-learn实现机器学习算法的应用&#xff0…...

Windows 自带的 Linux 子系统(WSL)安装与使用

WSL官网安装教程&#xff1a; https://learn.microsoft.com/zh-cn/windows/wsl/install Windows 自带的Linux子系统&#xff0c;比用VM什么的香太多了。可以自己看官方教程&#xff0c;也可以以下步骤完成。 如果中间遇到我没遇到的问题百度&#xff0c;可以在评论区评论&#…...

C语言--贪吃蛇

目录 1. 实现目标2. 需掌握的技术3. Win32 API介绍控制台程序控制台屏幕上的坐标COORDGetStdHandleGetConsoleCursorinfoCONSOLE_CURSOR_INFOSetConsoleCursorInfoSetConsoleCursorPositionGetAsyncKeyState 4. 贪吃蛇游戏设计与分析地图<locale.h>本地化类项setlocale函…...

原型设计工具Axure RP

Axure RP是一款专业的快速原型设计工具。Axure&#xff08;发音&#xff1a;Ack-sure&#xff09;&#xff0c;代表美国Axure公司&#xff1b;RP则是Rapid Prototyping&#xff08;快速原型&#xff09;的缩写。 下载链接&#xff1a;https://www.axure.com/ 下载 可以免费试用…...

HeadFirst读书笔记

一、设计模式入门 1、使用模式最好的方式“把模式装进脑子里&#xff0c;然后在你的设计和已有的应用中&#xff0c;寻找何处可以使用它们”。以往是代码复用&#xff0c;现在是经验复用。 2、软件开发的一个不变的真理就是变化。 二、设计原则 1、找出应用中可能需要变化之…...

【C++】---内存管理new和delete详解

一、C/C内存分布 C/C内存被分为6个区域&#xff1a; &#xff08;1&#xff09; 内核空间&#xff1a;存放内核代码和环境变量。 &#xff08;2&#xff09;栈区&#xff1a;向下增长&#xff08;存放非静态局部变量&#xff0c;函数参数&#xff0c;返回值等等&#xff09; …...

go-zero微服务入门教程

go-zero微服务入门教程 本教程主要模拟实现用户注册和用户信息查询两个接口。 准备工作 安装基础环境 安装etcd&#xff0c; mysql&#xff0c;redis&#xff0c;建议采用docker安装。 MySQL安装好之后&#xff0c;新建数据库dsms_admin&#xff0c;并新建表sys_user&#…...

蓝桥杯刷题--python-12

3768. 字符串删减 - AcWing题库 nint(input()) sinput() res0 i0 while(i<n): if s[i]x: ji1 while(j<n and s[j]x): j1 resmax(j-i-2,0) ij else: i1 print(res) 3777. 砖块 - AcWing题库 # https://www.a…...

LeetCode LCR 085.括号生成

正整数 n 代表生成括号的对数&#xff0c;请设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”] 示例 2&#xff1a; 输入&#x…...

抖音视频评论数据提取软件|抖音数据抓取工具

一、开发背景&#xff1a; 在业务需求中&#xff0c;我们经常需要下载抖音视频。然而&#xff0c;在网上找到的视频通常只能通过逐个复制链接的方式进行抓取和下载&#xff0c;这种操作非常耗时。我们希望能够通过关键词自动批量抓取并选择性地下载抖音视频。因此&#xff0c;为…...

【web】云导航项目部署及环境搭建(复杂)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、项目介绍1.1项目环境架构LNMP1.2项目代码说明 二、项目环境搭建2.1 Nginx安装2.2 php安装2.3 nginx配置和php配置2.3.1 修改nginx文件2.3.2 修改vim /etc/p…...

软件测试人员必会的linux命令

文件和目录操作: ● ls:列出目录中的文件和子目录。 ● cd:改变当前工作目录。 ● mkdir:创建新目录。 ● rm:删除文件或目录。 ● cp:复制文件或目录。 ● mv:移动或重命名文件或目录。 文本查看和编辑: ● cat:查看文件内容。 ● more或less:分页查看文件内…...

Mac使用K6工具压测WebSocket

commend空格 打开终端&#xff0c;安装k6 brew install k6验证是否安装成功 k6 version设置日志级别为debug export K6_LOG_LEVELdebug执行脚本&#xff08;进入脚本所在文件夹下&#xff09; k6 run --vus 100 --duration 10m --out csvresult.csv script.js 脚本解释&…...

小程序--vscode配置

要在vscode里开发微信小程序&#xff0c;需要安装以下两个插件&#xff1a; 安装后&#xff0c;即可使用vscode开发微信小程序。 注&#xff1a;若要实现鼠标悬浮提示&#xff0c;则需新建jsconfig.json文件&#xff0c;并进行配置&#xff0c;即可实现。 jsconfig.json内容如…...

linux僵尸进程

僵尸进程&#xff08;Zombie Process&#xff09;是指在一个进程终止时&#xff0c;其父进程尚未调用wait()或waitpid()函数来获取该进程的终止状态信息&#xff0c;导致进程的资源&#xff08;如进程表中的记录&#xff09;仍然保留在系统中的一种状态。 当一个进程结束时&am…...

【web | CTF】攻防世界 Web_php_unserialize

天命&#xff1a;这条反序列化题目也是比较特别&#xff0c;里面的漏洞知识点&#xff0c;在现在的php都被修复了 天命&#xff1a;而且这次反序列化的字符串数量跟其他题目不一样 <?php class Demo { // 初始化给变量内容&#xff0c;也就是当前文件&#xff0c;高亮显示…...

Vue3中的select 的option是多余的?

背景&#xff1a; 通过Vue3中填充一个下拉框&#xff0c;在打开页面时要指定默认选中&#xff0c;并在选项改变时把下拉框的选中值显示出来 问题&#xff1a; 填充通常的作法是设置 <option v-for"option in cities" :value"option.value" >&a…...

考研408深度分析+全年规划

408确实很难&#xff0c;他的难分两方面 一方面是408本身的复习难度&#xff0c;我们都知道&#xff0c;408的考察科目有四科&#xff0c;分别是数据结构&#xff0c;计算机组成原理&#xff0c;操作系统和计算机网络。大家回想一下自己在大学本科时候学习这些专业课的难度&am…...

【算法笔记】ch01_01_0771 宝石与石头

笔记介绍&#xff1a; 本项目是datawhale发布的LeetCode 算法笔记&#xff08;Leetcode-Notes&#xff09;课程完成笔记&#xff0c;根据推荐题目循序渐进练习算法题目。主要用python进行书写相关代码&#xff0c;会介绍解题思路及跑通解法。 0771. 宝石与石头 题目大意 描…...

jQuery瀑布流画廊,瀑布流动态加载

jQuery瀑布流画廊&#xff0c;瀑布流动态加载 效果展示 手机布局 jQuery瀑布流动态加载 HTML代码片段 <!-- mediabanner --><div class"mediabanner"><img src"img/mediabanner.jpg" class"bg"/><div class"text&qu…...

玩转ChatGPT:参考文献速查

一、写在前面 各位大佬&#xff0c;我又回来了&#xff0c;最近2月太忙啦&#xff08;过年、奶娃、本子、材料、结题&#xff09;&#xff0c;断更了。现水一篇证明我还活着&#xff01;&#xff01;&#xff01; 最近在写国自然本子&#xff0c;遇到一个估计大家都会遇到的问…...

[设计模式Java实现附plantuml源码~行为型]算法的封装与切换——策略模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…...

​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】

欢迎来CILMY23的博客喔&#xff0c;本期系列为​【C语言】长篇详解&#xff0c;字符系列篇3-----strstr&#xff0c;strtok&#xff0c;strerror字符串函数的使用【图文详解​】&#xff0c;图文讲解各种字符串函数&#xff0c;带大家更深刻理解C语言中各种字符串函数的应用&am…...

苏州最新新闻事件今天/seo引流什么意思

允许一组线程全部等待彼此达到共同屏障点的同步辅助。 循环阻塞在涉及固定大小的线程方的程序中很有用&#xff0c;这些线程必须偶尔等待彼此。 屏障被称为循环 &#xff0c;因为它可以在等待的线程被释放之后重新使用。 它的作用就是会让所有线程都等待完成后才会继续下一步行…...

eclipse可以做网站嘛/网络链接推广

有些童鞋买了车载GPS定位器不知道怎么去安装&#xff0c;去了汽车4S店问了安装价格&#xff0c;结果吓了一大跳&#xff0c;便宜的80&#xff0c;贵则200多&#xff0c;几乎就可以再买一个GPS定位器了&#xff0c;那么GPS定位器可不可以自己动手安装呢&#xff1f;答案是肯定的…...

安全者 wordpress/网络营销策略包括

前面文章介绍了前端路由简单实现和Pjax入门方面的文章&#xff0c;今天来分享一个单页面应用神器jquery.pjax.js。 HTML 我们准备一个加载div#loading,默认隐藏&#xff0c;ajax请求的时候才显示。#container是用来加载响应的页面内容。.pagination是分页条组件。 <div clas…...

网站登录界面模板/夸克搜索

参考网址1&#xff1a; iOS消息推送机制的实现 http://www.cnblogs.com/qq78292959/archive/2012/07/16/2593651.html 参考网址2&#xff1a; iOS 推送的服务端实现 http://www.dozer.cc/2013/03/push-notifications-server-side-implement/ 参考网址3&#xff1a; iOS 证书与推…...

做网站有什么用/卡点视频软件下载

在JAVA编程中&#xff0c;有这样一个问题&#xff1a;类A引用了类B&#xff0c;那么如果运行时没有类B&#xff0c;类A会不会调用成功&#xff0c;而不抛异常&#xff1f; 答案是&#xff0c;有可能运行成功&#xff0c;而不抛异常。 例子1&#xff1a; ClassA.java packag…...

如何做条形码网站怎么搞/大一网页设计作业成品

首先&#xff0c;网站的工具超级丰富&#xff0c;涵盖面好广&#xff0c;包含有图片类工具&#xff0c;数据换算&#xff0c;生活娱乐&#xff0c;教育&#xff0c;文本工具&#xff0c;开发工具&#xff0c;文档转换&#xff0c;视频&#xff0c;PDF转换工具等等9个大类&#…...