Python基础之协程

文章目录

  • 1 协程
    • 1.1 简介
    • 1.2 协程优势&分类
      • 1.2.1 优势
      • 1.2.2 分类
    • 1.3 generator协程
    • 1.4 gevent协程
    • 1.5 asyncio
      • 1.5.1 简介
      • 1.5.2 asyncio函数
      • 1.5.3 async\await
      • 1.5.4 asyncio基本操作
        • 1.5.4.1 asyncio协程对象
        • 1.5.4.2 task对象
        • 1.5.4.3 future对象
        • 1.5.4.4 绑定回调
        • 1.5.4.5 异步多任务
        • 1.5.4.6 asyncio.gather和asyncio.wait 区别

1 协程

1.1 简介

协程,又称微线程,纤程。英文名Coroutine
协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用。
子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。
所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。
子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。
协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B:

def A():print '1'print '2'print '3'def B():print 'x'print 'y'print 'z'假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:1
2
x
y
3
z

但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。

看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何优势?

1.2 协程优势&分类

1.2.1 优势

  • 最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
  • 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
  • 因为协程是一个线程执行,那怎么利用多核CPU呢,最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

1.2.2 分类

协程有两种,一种无栈协程,python中以 asyncio 为代表, 一种有栈协程,python 中 以 gevent 为代表

有栈线程无栈线程备注
示例lua thread
python gevent
C# yield return
C# async\await
python asyncio
是否拥有单独的上下文上下文包括寄存器、栈帧
局部变量保存位置无栈协程的局部变量保存在堆上,比如generator的数据成员
优点1. 每个协程有单独的上下文,可以在任意的嵌套函数中任何地方挂起此协程。
2. 不需要编译器做语法支持,通过汇编指令即可实现
1. 不需要为每个协程保存单独的上下文,内存占用低。
2. 切换成本低,性能更高
缺点1. 需要提前分配一定大小的堆内存保存每个协程上下文,所以会出现内存浪费或者栈溢出。
2. 上下文拷贝和切换成本高,性能低于无栈协程
1. 需要编译器提供语义支持,比如C# yield return语法糖。
2. 只能在这个生成器内挂起此协程,无法在嵌套函数中挂起此协程。
3. 关键字有一定传染性,异步代码必须都有对应的关键字。作为对比,有栈协程只需要做对应的函数调用
无栈协程无法在嵌套函数中挂起此协程,有栈协程由于是通过保存和切换上下文包括寄存器和执行栈实现,可以在协程函数的嵌套函数内部yield这个协程并唤醒。

1.3 generator协程

Python对协程的支持还非常有限,用在generator中的yield可以一定程度上实现协程。虽然支持不完全,但已经可以发挥相当大的威力了。

来看例子:
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:

import timedef consumer():r = ''while True:n = yield rif not n:returnprint('[CONSUMER] Consuming %s...' % n)time.sleep(1)r = '200 OK'def produce(c):next(c)n = 0while n < 5:n = n + 1print('[PRODUCER] Producing %s...' % n)r = c.send(n)print('[PRODUCER] Consumer return: %s' % r)c.close()if __name__=='__main__':c = consumer()produce(c)执行结果:
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK

注意到consumer函数是一个generator(生成器),把一个consumer传入produce后:

  • 首先调用c.next()启动生成器;
  • 然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
  • consumer通过yield拿到消息,处理,又通过yield把结果传回;
  • produce拿到consumer处理的结果,继续生产下一条消息;
  • produce决定不生产了,通过c.close()关闭consumer,整个过程结束。

整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为协程,而非线程的抢占式多任务。

1.4 gevent协程

Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。
gevent是第三方库,通过greenlet实现协程,其基本思想是:

当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:

from gevent import monkey; spawn
monkey.patch_all()  # 替换标准库以使用 gevent 兼容的版本def f(n):for i in range(n):print(gevent.getcurrent(), i)g1 = spawn(f, 5)
g2 = spawn(f, 5)
g3 = spawn(f, 5)
g1.join()
g2.join()
g3.join()运行结果:
<Greenlet at 0x10e49f550: f(5)> 0
<Greenlet at 0x10e49f550: f(5)> 1
<Greenlet at 0x10e49f550: f(5)> 2
<Greenlet at 0x10e49f550: f(5)> 3
<Greenlet at 0x10e49f550: f(5)> 4
<Greenlet at 0x10e49f910: f(5)> 0
<Greenlet at 0x10e49f910: f(5)> 1
<Greenlet at 0x10e49f910: f(5)> 2
<Greenlet at 0x10e49f910: f(5)> 3
<Greenlet at 0x10e49f910: f(5)> 4
<Greenlet at 0x10e49f4b0: f(5)> 0
<Greenlet at 0x10e49f4b0: f(5)> 1
<Greenlet at 0x10e49f4b0: f(5)> 2
<Greenlet at 0x10e49f4b0: f(5)> 3
<Greenlet at 0x10e49f4b0: f(5)> 4

可以看到,3个greenlet是依次运行而不是交替运行。

要让greenlet交替运行,可以通过gevent.sleep()交出控制权:

def f(n):for i in range(n):print (gevent.getcurrent(), i)gevent.sleep(0)执行结果:<Greenlet at 0x10cd58550: f(5)> 0
<Greenlet at 0x10cd58910: f(5)> 0
<Greenlet at 0x10cd584b0: f(5)> 0
<Greenlet at 0x10cd58550: f(5)> 1
<Greenlet at 0x10cd584b0: f(5)> 1
<Greenlet at 0x10cd58910: f(5)> 1
<Greenlet at 0x10cd58550: f(5)> 2
<Greenlet at 0x10cd58910: f(5)> 2
<Greenlet at 0x10cd584b0: f(5)> 2
<Greenlet at 0x10cd58550: f(5)> 3
<Greenlet at 0x10cd584b0: f(5)> 3
<Greenlet at 0x10cd58910: f(5)> 3
<Greenlet at 0x10cd58550: f(5)> 4
<Greenlet at 0x10cd58910: f(5)> 4
<Greenlet at 0x10cd584b0: f(5)> 4

3个greenlet交替运行,把循环次数改为500000,让它们的运行时间长一点,然后在操作系统的进程管理器中看,线程数只有1个。
当然,实际代码里,我们不会用gevent.sleep()去切换协程,而是在执行到IO操作时,gevent自动切换,代码如下:

from gevent import monkey
monkey.patch_all()import gevent
import urllib.requestdef f(url):print('GET: {}'.format(url))resp = urllib.request.urlopen(url)data = resp.read()print('{} bytes received from {}.'.format(len(data), url))gevent.joinall([gevent.spawn(f, 'https://www.python.org/'),gevent.spawn(f, 'https://www.yahoo.com/'),gevent.spawn(f, 'https://github.com/'),
])运行结果:GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
45661 bytes received from https://www.python.org/.
14823 bytes received from https://github.com/.
304034 bytes received from https://www.yahoo.com/.

从结果看,3个网络操作是并发执行的,而且结束顺序不同,但只有一个线程。

注意gevent只能在Unix/Linux下运行,在Windows下不保证正常安装和运行。

1.5 asyncio

1.5.1 简介

asyncio 是用来编写并发代码的库,使用 async/await 语法。
asyncio 被用作多个提供高性能 Python 异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等。
asyncio 往往是构建 IO 密集型和高层级 结构化 网络代码的最佳选择。

1.5.2 asyncio函数

使用协程中的一般概念:

  • event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行
  • coroutine:协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用。可以使用async关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象
  • task:任务,它是对协程对象的进一步封装,包含了任务的各个状态
  • future:代表将来执行或还没有执行的任务,实际上和task没有本质区别
  • async:定义一个协程,不会立即执行
  • await:用来挂起阻塞方法的执行

事件循环函数(包括循环的创建、运行和停止)

  • asyncio.get_running_loop():函数返回当前 OS 线程中正在运行的事件循环。
  • asyncio.get_event_loop():函数获取当前事件循环。
  • asyncio.set_event_loop(loop):函数将 loop 设置为当前 OS 线程的当前事件循环。
  • asyncio.new_event_loop():函数创建一个新的事件循环。
  • loop.run_until_complete(future):函数运行直到 future (Future 的实例) 被完成。
  • loop.run_forever():函数运行事件循环直到 stop() 被调用。
  • loop.stop():函数停止事件循环。
  • loop.is_running():函数返回 True 如果事件循环当前正在运行。
  • loop.is_closed():函数如果事件循环已经被关闭,返回 True 。
  • loop.close():函数关闭事件循环。
  • loop.create_future():函数创建一个附加到事件循环中的 asyncio.Future 对象。
  • loop.create_task(coro, *, name=None):函数安排一个 协程 的执行。返回一个 Task 对象。
  • loop.set_task_factory(factory):函数设置一个 task 工厂 , 被用于 loop.create_task()
  • loop.get_task_factory():函数返回一个任务工厂,或者如果是使用默认值则返回 None。

1.5.3 async\await

async 关键字:

  • async 用于声明一个函数为异步函数。这意味着该函数在执行过程中可能会遇到需要等待的操作(如I/O操作、网络请求、等待用户输入等),而不会阻塞整个程序的执行。
  • 异步函数内部可以包含await表达式,用于等待其他异步操作完成。

await 关键字:

  • await用于等待一个异步操作(即一个FutureTask、或其他可等待对象,如另一个异步函数)的完成。它只能在异步函数内部使用。
  • await表达式被调用时,它会暂停当前异步函数的执行,直到等待的异步操作完成。在等待期间,事件循环会继续运行,并可能执行其他异步任务。
  • 一旦等待的异步操作完成,await表达式会获取其结果(或捕获其抛出的异常),然后异步函数会从await表达式之后继续执行。

挂起与恢复:

  • await一个异步操作时,当前协程(即异步函数)会被挂起,控制权交还给事件循环。事件循环可以调度其他协程运行,包括正在等待的异步操作本身。
  • 当异步操作完成时,事件循环会恢复挂起的协程,并从await表达式之后继续执行。

关于await后面跟的函数的性质:

  • await后面必须跟一个可等待对象,这通常是一个异步函数(用async def定义)的调用结果,因为它会返回一个Future或Task对象,这些对象是可等待的。
  • 如果await后面跟的不是异步函数或可等待对象,那么Python解释器会抛出一个TypeError,因为非异步函数或不可等待对象不能被await。
  • 调用非异步函数时,如果该函数执行时间较长,那么它会阻塞当前线程(在asyncio中,这通常意味着阻塞当前协程的事件循环),直到函数执行完成。因此,在异步编程中,应该尽量避免在异步函数内部调用非异步的、可能阻塞的函数。

1.5.4 asyncio基本操作

1.5.4.1 asyncio协程对象

使用async定义一个协程对象,并创建一个事件循环对象

import asyncio
#定义协程对象
async def get_request(url):print("正在请求的url是:",url)print('请求成功的url:',url)return url
#得到协程对象
coroutine_obj=get_request('www.baidu.com')
#创建一个事件循环对象
loop=asyncio.get_event_loop()
#将协程对象注册到loop中,并启动loop
loop.run_until_complete(coroutine_obj)
loop.close()
1.5.4.2 task对象

task对象需要loop对象基础上建立起来

import asyncio
#定义协程对象
async def get_request(url):print("正在请求的url是:",url)print('请求成功的url:',url)return url
#得到协程对象
coroutine_obj=get_request('www.baidu.com')#创建一个事件循环对象
loop=asyncio.get_event_loop()
#基于loop创建了一个task对象
task=loop.create_task(coroutine_obj)
print(task)
#基于loop注册任务
loop.run_until_complete(task)
print(task)
loop.close()
1.5.4.3 future对象

主要函数:

  • asyncio.Future(*, loop=None):函数是一个 Future 代表一个异步运算的最终结果。线程不安全。
  • asyncio.isfuture(obj):函数用来判断如果 obj 为一个 asyncio.Future 类的示例、 asyncio.Task 类的实例或者一个具有 _asyncio_future_blocking 属性的对象,返回 True。
  • asyncio.ensure_future(obj, *, loop=None):函数创建新任务。
  • asyncio.wrap_future(future, *, loop=None):函数将一个 concurrent.futures.Future 对象封装到 asyncio.Future 对象中。

Future 对象相关函数:

  • fut.result():函数返回 Future 的结果。
  • fut.set_result(result):函数将 Future 标记为 完成 并设置结果。
  • fut.set_exception(exception):函数将 Future 标记为 完成 并设置一个异常。
  • fut.done():函数如果 Future 为已 完成 则返回 True 。
  • fut.cancelled():函数是如果 Future 已取消则返回 True
  • fut.add_done_callback(callback, *, context=None):函数添加一个在 Future 完成 时运行的回调函数。
  • fut.remove_done_callback(callback):函数从回调列表中移除 callback 。
  • fut.cancel():函数取消 Future 并调度回调函数。
  • fut.exception():函数返回 Future 已设置的异常。
  • fut.get_loop():函数返回 Future 对象已绑定的事件循环。

future对象与task对象不同的是创建基于asyncio空间来创建的

import asyncio
#定义协程对象
async def get_request(url):print("正在请求的url是:",url)print('请求成功的url:',url)return url
#得到协程对象
coroutine_obj=get_request('www.baidu.com')#创建一个事件循环对象
loop=asyncio.get_event_loop()
#基于loop创建了一个task对象
future=asyncio.ensure_future(coroutine_obj)
print(future)
loop.run_until_complete(future)
print(future)
loop.close()

或者示例:

import sys
import asyncio
import time# 一个对future进行赋值的函数
async def slow_operation(future, num):await asyncio.sleep(1)# 给future赋值future.set_result('Future'+ str(num) +' is done!')def main():loop = asyncio.get_event_loop()# 创建一个futurefuture1 = loop.create_future()# 使用ensure_future 创建Taskasyncio.ensure_future(slow_operation(future1, 1))future2 = loop.create_future()asyncio.ensure_future(slow_operation(future2, 2))# gather Tasks,并通过run_uniti_complete来启动、终止looploop.run_until_complete(asyncio.gather(future1, future2))print(future1.result())print(future2.result())loop.close()if __name__ == "__main__":main()
1.5.4.4 绑定回调

在使用task或者future绑定回调时,需要先定义回调函数
回调函数中返回的result方法就是任务对象中封装的协程对象对应的函数返回值
注意:回调函数必须有返回值,不然result方法就没有值

def callback_func(task):print(task.result())

在使用task或者future绑定回调时,都可以使用方法绑定task.add_done_callback(callback_func)

import asyncio
#定义协程对象
async def get_request(url):print("正在请求的url是:",url)print('请求成功的url:',url)return url
#得到协程对象
coroutine_obj=get_request('www.baidu.com')
loop=asyncio.get_event_loop()
future=asyncio.ensure_future(coroutine_obj)
#把回调函数绑定到任务对象中
future.add_done_callback(callback_func)
loop.run_until_complete(future)
loop.close()
1.5.4.5 异步多任务

在一个异步函数中,可以不止一次挂起,也就是可以用多个await
多任务时,对于run_until_complete方法需要这样用asyncio.wait()方法处理:loop.run_until_complete(asyncio.wait(task_list))
代码示例:

import time
import asyncio
async def get_request(url):print("正在请求的url是:",url)#在异步协程中如果出现了同步模块相关代码,那么就无法实现异步# time.sleep(2)#当在asyncio中遇到阻塞操作就必须进行手动挂起await asyncio.sleep(2)print('请求成功的url:',url)    
start_time=time.time()
urls=['www.baidu.com','www.sogou.com','www.goubanjia.com']#任务列表
task_list=[]
for url in urls:coroutine_obj=get_request(url)future=asyncio.ensure_future(coroutine_obj)task_list.append(future)
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(task_list))
loop.close()
print(time.time()-start_time)
1.5.4.6 asyncio.gather和asyncio.wait 区别

asyncio.gatherasyncio.wait都是asyncio库中用于处理异步任务(协程)的重要函数,但它们之间存在一些关键的区别。以下是这两个函数之间的主要差异:

  • 返回值
    • asyncio.gather:该函数并发运行多个协程,并等待它们全部完成。它返回一个Future对象,当所有协程都完成时,这个Future对象的结果是一个包含所有协程返回值的列表(列表的顺序与传入的协程列表相同)。如果任何一个协程抛出异常,则gather会立即取消所有其他仍在运行的协程,并重新抛出那个异常。
    • asyncio.wait:该函数也是用于并发运行多个协程,但它返回的是一个包含两个集合的元组:done和pending。done是一个已完成的协程(Future或Task)列表,而pending是一个尚未完成的协程列表。这意味着需要从done列表中的每个协程中调用result()方法来获取结果,而pending列表中的协程则需要你进一步处理或等待。
  • 使用场景
    • asyncio.gather:当需要同时运行多个协程并立即获取它们的结果时,gather是一个很好的选择。它简化了结果收集的过程,并允许同步的方式处理异步操作的结果。
    • asyncio.wait:当需要更细粒度的控制时,比如在某个超时后取消未完成的协程,或者要分别处理已完成和未完成的协程时,wait是一个更合适的选择。然而,使用wait需要更多的手动操作来收集结果或处理未完成的协程。
  • 额外功能
    • asyncio.gather:除了基本的并发执行和结果收集功能外,gather还支持一些额外的参数,如return_exceptions(默认为False),当设置为True时,如果协程抛出异常,异常会被捕获并作为结果列表中的一项返回,而不是中断整个gather操作。
    • asyncio.waitwait函数也接受一些参数,如timeout(指定等待时间,默认为None,表示无限期等待)和return_when(指定在什么条件下返回,如FIRST_COMPLETED、ALL_COMPLETED等)。这些参数提供了对协程执行过程的更多控制。

以下是使用asyncio.gather和asyncio.wait的示例代码:

import asyncio  async def task(name, delay):  print(f"{name} started")  await asyncio.sleep(delay)  return f"{name} finished"  async def main():  # 使用 asyncio.gather  tasks = [task("A", 1), task("B", 2), task("C", 3)]  results = await asyncio.gather(*tasks)  print(results)  # 输出: ['A finished', 'B finished', 'C finished']  # 使用 asyncio.wait  tasks = [task("D", 1), task("E", 2), task("F", 3)]  done, pending = await asyncio.wait(tasks)  for d in done:  print(d.result())  # 分别打印每个已完成协程的结果  # 处理 pending 列表中的协程(这里只是打印出来)  for p in pending:  print(f"Pending: {p}")  asyncio.run(main())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/42073.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的生日聚会(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

使用antd的<Form/>组件获取富文本编辑器输入的数据

前端开发中&#xff0c;嵌入富文本编辑器时&#xff0c;可以通过富文本编辑器自身的事件处理函数将数据传输给后端。有时候&#xff0c;场景稍微复杂点&#xff0c;比如一个输入页面除了要保存富文本编辑器的内容到后端&#xff0c;可能还有一些其他输入组件获取到的数据也一并…

Mac搭建anaconda环境并安装深度学习库

1. 下载anaconda安装包 根据自己的操作系统不同&#xff0c;选择不同的安装包Anaconda3-2024.06-1-MacOSX-x86_64.pkg&#xff0c;我用的还是旧的intel所以下载这个&#xff0c;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/&#xff0c;如果mac用的是M1&#xff0…

GuLi商城-商品服务-API-品牌管理-云存储开通与使用

这里学习下阿里云对象存储 地址&#xff1a;对象存储 OSS_云存储服务_企业数据管理_存储-阿里云 登录支付宝账号&#xff0c;找到了我以前开通的阿里云对象存储 熟悉下API 文档中心 简介_对象存储(OSS)-阿里云帮助中心 我们将用这种方式上传阿里云OSS

SprongBoot3整合Knife4j实现在线接口文档

大家好&#xff0c;我是晓凡。 写在前面 在上一篇文章&#xff0c;我们详细介绍了SpringBoot3 怎么整合SpringDoc实现在线接口文档。但是&#xff0c;有不少小伙伴 都觉得接口界面太丑了。有没有什么更美观一点的UI界面呢&#xff1f; 当然是有的了&#xff0c;毕竟这是一个…

那些年,学过的屠龙术

朱泙漫学屠龙于支离益&#xff0c;单千金之家&#xff0c;三年技成而无所用其巧。 程序员的技能&#xff0c;比如&#xff1a; Windows平台编程&#xff0c;从入行业时至今&#xff0c;没有做过相关的业务&#xff0c;相关的知识只能当成谈资。 Windows平台下应用的crash问题&…

基于Android Studio电影购票系统

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 主要实为了方便用户随时随地进行电影购票。在配色方面选择了一些富有电影元素的颜色。主要能够实现的功能与流程为&#xff1a; 1.用户首先需要注册用户名填写密码。 2.用户可以用之前注册的用户名和密码进行登录。 3.登…

数据库——数据库性能优化

title: 数据库——数据库性能优化 date: 2024-07-06 12:26:21 tags: 数据库 categories: 数据库 cover: /image/T1.jpg description: 数据库——MySQL的性能优化 数据库优化 系统的吞吐量瓶颈往往出现在数据库的 访问速度 上&#xff0c;随着应用程序的运行&#xff0c;数据库…

【密码学】密码学体系

密码学体系是信息安全领域的基石&#xff0c;它主要分为两大类&#xff1a;对称密码体制和非对称密码体制。 一、对称密码体制&#xff08;Symmetric Cryptography&#xff09; 在对称密码体制中&#xff0c;加密和解密使用相同的密钥。这意味着发送方和接收方都必须事先拥有这…

kubeadm离线部署kubernetesv1.30.0

背景&#xff1a;最近由于docker image获取镜像受限的问题&#xff0c;以及公司内部部署kubernetes受限于内部网络无法访问公网的问题&#xff0c;对于离线部署kubernetes成为不是十分方便。谨以此文仅供参考。 kubernetes部署节点信息 kubernetes版本 1.30.0 操作系统版本&a…

springboot2.7.6 集成swagger

在 Spring Boot 2.7.6 版本中集成 Swagger 的步骤相对直接&#xff0c;主要涉及添加依赖、编写配置以及在控制器中添加文档注解几个环节。 下面是集成 Swagger 的基本步骤&#xff1a; 1. 添加依赖 首先&#xff0c;在pom.xml文件中添加 Swagger 相关依赖。 对于 Spring Boot…

1-3 NLP为什么这么难做

1-3 NLP为什么这么难做 主目录点这里 字词结构的复杂性 中文以汉字为基础单位&#xff0c;一个词通常由一个或多个汉字组成&#xff0c;而不像英语词汇单元由字母构成。这使得中文分词&#xff08;切分句子为词语&#xff09;成为一个具有挑战性的任务。语言歧义性 中文中常…

提升机硬件设备的技术规格文档

提升机硬件设备技术规格文档 1. 产品概述 本技术规格文档旨在详细描述[公司名称]生产的[型号]型提升机的硬件设备技术参数、性能指标、安全特性及安装维护要求。该提升机设计用于[具体应用场景&#xff0c;如高层建筑、仓库货物搬运、家用等]&#xff0c;旨在提供高效、安全、…

网络安全设备——蜜罐

网络安全设备蜜罐&#xff08;Honeypot&#xff09;是一种主动防御技术&#xff0c;它通过模拟真实网络环境中的易受攻击的目标&#xff0c;以吸引和监测攻击者的活动。具体来说&#xff0c;蜜罐是一种虚拟或实体的计算机系统&#xff0c;它模拟了一个真实的网络系统或应用程序…

AcWing 3207:门禁系统 ← 桶排序中“桶”的思想

【题目来源】https://www.acwing.com/problem/content/3210/【题目描述】 涛涛最近要负责图书馆的管理工作&#xff0c;需要记录下每天读者的到访情况。 每位读者有一个唯一编号&#xff0c;每条记录用读者的编号来表示。 给出读者的来访记录&#xff0c;请问每一条记录中的读者…

C语言的结构体写法

C语言中的结构体是一种用户自定义的数据类型&#xff0c;它可以同时存储多个不同类型的数据。结构体由多个成员变量组成&#xff0c;每个成员变量可以有不同的数据类型。 结构体的定义形式为&#xff1a; struct 结构体名 {数据类型 成员变量1;数据类型 成员变量2;// 其他成员…

Shell编程类-网站检测

Shell编程类-网站检测 面试题参考答法 a(1 2 3 4) echo ${a[0]} echo ${a[*]}这里声明一个数值&#xff0c;并选择逐个调用输出还是全部输出 curl -w %{http_code} urL/IPADDR常用-w选项去判断网站的状态&#xff0c;因为不加选择访问到的网站可能出现乱码无法判断是否网站down…

Xilinx FPGA:vivado关于fifo的一些零碎知识

一、FIFO概念 先进先出&#xff0c;是一种组织和操作数据结构的方法。在硬件应用中&#xff0c;FIFO一般由一些读写指针&#xff0c;存储和控制的逻辑组成。 二、xilinx中生成的FIFO的存储类型 &#xff08;1&#xff09;shift register FIFO : 移位寄存器FIFO&#xff0c;这…

【基础篇】1.8 C语言基础(二)更新中

三,位操作 在STM32开发中,位操作是非常重要的技术,特别是在处理器外设寄存器的配置和控制过程中。它可以有效地提高代码的性能、可维护性和可移植性。 3.1 位操作运算符 3.1.1 按位与(&) 运算规则:如果两个操作数的对应位都为 1,则结果为 1;否则为 0。示例:a &…

自动化设备上位机设计 三

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 using SqlSugar;namespace 自动化上位机设计 {public partial class Form1 : Form{SqlHelper sqlHelper new SqlHelper();SqlSugarClient dbContent null;bool IsRun false;int Count 0;public Form1(){Initializ…