python进阶篇-day07-进程与线程

day06进程与线程

一. 进程

每个软件都可以看作是一个进程(数据隔离)

软件内的多个任务可以看作是多个线程(数据共享)

单核CPU: 宏观并行, 微观并发

真正的并行必须有多核CPU

多任务介绍

概述

多任务指的是, 多个任务"同时"执行

目的

节约资源, 充分利用CPU资源, 提高效率

表现形式

并发

针对于单核CPU来讲, 如果有多个任务同时请求执行, 但是同一瞬间CPU只能执行1个(任务), 于是就安排他们交替执行.

因为时间间隔非常短, 所以宏观上看是并行, 但是微观上还是并发的.

并行

针对多核CPU来讲, 多个任务可以同时执行

进程介绍

概述

进程: 指的是可执行程序(*.exe), 也是CPU分配资源的最小单位.

线程: 进程的执行路径, 执行单元, 也是CPU调度资源的最小单位

解释:

进程: 车

线程: 车道

多进程实现

步骤

  1. 导包(multiprocessing)

  2. 创建进程对象, 关联 该进程要执行的任务(函数)

  3. 启动进程执行任务

代码
import multiprocessing, time
​
​
# 定义代码函数
def coding():
    for i in range(1, 21):
        time.sleep(1)
        print(f'正在敲代码----  {i}')
​
        
# 定义音乐函数
def music():
    for i in range(1, 21):
        time.sleep(1)
        print(f'正在听音乐****  {i}')
​
​
if __name__ == '__main__':
    # 创建进程对象
    p1 = multiprocessing.Process(target=coding)
    p2 = multiprocessing.Process(target=music)
    # 执行进程
    p1.start()
    p2.start()

进程参数

参数

target: 用于关联 进程要执行的任务的.name: 进程名, 默认是: Process-1, Process-2,...., 可以手动修改, 一 般不改.args: 可以通过 元组 的形式传递参数, 实参的个数 及 对应的数据类型 要和 形参的个数及类型 一致.kwargs: 可以通过 字典 的形式传递参数, 实参的个数 要和 形参的个数 一致.

代码演示
import multiprocessing, time
​
​
def coding(name, num):
    for i in range(1, num):
        time.sleep(0.01)
        print(f'{name}正在敲第{i}行代码-')
​
​
def music(name, num):
    for i in range(1, num):
        time.sleep(0.01)
        print(f'{name}正在听第{i}首音乐********')
​
​
if __name__ == '__main__':
    p1 = multiprocessing.Process(target=coding, args=('小明', 21))
    p2 = multiprocessing.Process(target=music, kwargs={'num': 21, 'name': '小明'})
    p11 = multiprocessing.Process(target=coding, args=('小明', 21), name='QQ')
    p22 = multiprocessing.Process(target=music, kwargs={'num': 21, 'name': '小明'}, name='WX')
    print(f'p1:{p1.name}')
    print(f'p2:{p2.name}')
    print(f'p11:{p11.name}')
    print(f'p22:{p22.name}')
    p1.start()
    p2.start()
​

main进程

解释

main程序入口也相当于一个进程, 在程序执行时遇到自定义进程会发生资源抢占, 上述代码中在输出进程对象后, 进程才启动, 所以上述的自定义进程不会和main进程强制资源, 并且自定义进程的启动需要一定时间, 此时的main进程可能已经完成自己的任务, 执行自定义进程.

图解

代码
import multiprocessing, time
​
​
def coding(name, num):
    for i in range(1, num):
        time.sleep(0.01)
        print(f'{name}正在敲第{i}行代码-')
​
​
def music(name, num):
    for i in range(1, num):
        time.sleep(0.01)
        print(f'{name}正在听第{i}首音乐********')
​
​
if __name__ == '__main__':
    p1 = multiprocessing.Process(target=coding, args=('小明', 11))
    p2 = multiprocessing.Process(target=music, kwargs={'num': 11, 'name': '小明'})
    # p11 = multiprocessing.Process(target=coding, args=('小明', 11), name='QQ')
    # p22 = multiprocessing.Process(target=music, kwargs={'num': 11, 'name': '小明'}, name='WX')
    p1.start()
    p2.start()
    # print(f'p1:{p1.name}')
    # time.sleep(0.1)
    # print(f'p2:{p2.name}')
    # time.sleep(0.1)
    # print(f'p11:{p11.name}')
    # time.sleep(0.1)
    # print(f'p22:{p22.name}')
​
    for i in range(1, 21):
        print(f'main: -------  {i}')
        time.sleep(0.1)

注: 上述代码中, 若将main中的循环放到自定义进程前, 则自定义进程不会和main抢占资源, 因为执行完main中的循环才执行到自定义进程, 可以把main函数看作栈, 从上到下执行

代码执行结果(每次可能都不一样)

进程编号

前面创建的进程为main进程的子进程, main为前面进程的父进程,

main的父进程为pycharm

程序执行默认有main函数

在操作系统中, 每个进程都有自己唯一的ID, 且当前进程被终止时, 该ID会被回收, 即: ID是可以重复使用的.

目的

  1. 查找子进程是由那个父进程创建的, 即: 找子进程和父进程的ID.

  2. 方便我们维护进程, 例如: kill -9 pid值 可以强制杀死进程.

当前编号

方式1: os模块的 getpid()方法方式2: multiprocessing模块的current_process()方法的 pid属性

当前的父进程编号

os模块的 getppid()方法 parent process: 父进程

代码演示

# 导包
import multiprocessing, time, os
​
​
# 案例: 小明一边敲着第n行代码, 一边听着第n首音乐.
# 1. 定义函数, 表示: 敲代码.
def code(name, num):
    for i in range(1, num + 1):
        print(f'{name} 正在敲第 {i} 行代码...')
        time.sleep(0.1)
        # multiprocessing模块的current_process()函数: 获取当前进程对象
        print(f'当前进程(p1)的id: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程的id为: {os.getppid()}')
​
​
# 2. 定义函数, 表示: 听音乐
def music(name, count):
    for i in range(1, count + 1):
        print(f'{name} 正在听第 {i} 首音乐.........')
        time.sleep(0.1)
        print(f'当前进程(p2)的id: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程的id为: {os.getppid()}')
​
​
# 在main中测试.
if __name__ == '__main__':
    # 3. 创建进程对象.
    # Process类的参数: target: 关联的函数名, name: 当前进程的名字, args:元组的形式传参. kwargs: 字典的形式传参.
    p1 = multiprocessing.Process(name='Process_QQ', target=code, args=('乔峰', 10))
    p2 = multiprocessing.Process(name='Process_Wechat', target=music, kwargs={'count': 10, 'name': '虚竹'})
​
    # print(f"p1进程的名字: {p1.name}")
    # print(f"p2进程的名字: {p2.name}")
​
    # 4. 启动进程.
    p1.start()
    p2.start()
​
    print(f'当前进程(main)的id: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程的id为: {os.getppid()}')

执行效果

进程注意事项

关于进程, 你要记忆的内容: 1. 进程之间数据是相互隔离的. 例如: 微信(进程) 和 QQ(进程)之间, 数据就是相互隔离的.

  1. 默认情况下, 主进程会等待它所有的子进程 执行结束再结束.

细节: 多进程之间, 针对于 main进程的(外部资源), 每个子进程都会拷贝一份, 进行执行.

数据隔离

代码
import multiprocessing, time
​
# 需求: 定义1个列表, 然后定义两个函数, 分别往列表中添加数据, 获取数据. 
# 之后用两个进程关联这个两个函数, 启动进程并观察结果.
# 1. 定义全局变量 my_list
my_list = []
print('我是main外资源, 看看我执行了几遍!')   # 执行三次
​
# 2. 定义函数, 实现往列表中添加数据.
def write_data():
    for i in range(1, 6):
        # 具体的添加元素的动作
        my_list.append(i)
        # 打印添加动作.
        print(f'添加 {i} 成功!')
    # 细节: 添加数据完毕后, 打印结果.
    print(f'write_data函数: {my_list}')
​
# 3. 定义函数, 实现从列表中获取数据.
def read_data():
    # 休眠 3 秒, 确保 write_data函数执行完毕.
    time.sleep(3)
    # 打印结果.
    print(f'read_data函数: {my_list}')
​
​
# 在main中测试
if __name__ == '__main__':
    # 4. 创建进程对象.
    p1 = multiprocessing.Process(target=write_data)
    p2 = multiprocessing.Process(target=read_data)
​
    # 5. 启动进程
    p1.start()
    p2.start()
​
    print('我是main内资源, 看看我执行了几遍!')   # 执行一次
执行结果

(默认)主进程等待子进程结束

演示
import multiprocessing, time
​
​
# 需求: 设置子进程执行3秒, 主进程执行1秒, 观察效果.
# 1. 定义方法, 用于关联子进程的.
def my_method():
    for i in range(10):
        print(f'工作中... {i}')
        time.sleep(0.3)  # 总休眠时间 = 0.3 * 10 = 3秒
​
​
# 2. 在main方法中测试.
if __name__ == '__main__':
    # 3. 创建子进程对象, 并启动.
    p1 = multiprocessing.Process(target=my_method)
    p1.start()  # 3秒后结束.
​
    # 4. 主进程执行1秒后结束.
    time.sleep(1)
​
    # 5. 打印主进程的结束提示.
    print('主进程 main 执行结束!')
解决
  1. 设置子进程为守护进程 p1.daemon = True 推荐

    类似于: 骑士(守护) 和 公主(非守护)

  2. 手动关闭子进程 p1.terminate() 不推荐(僵尸进程)

import multiprocessing, time
​
​
# 需求: 设置子进程执行3秒, 主进程执行1秒, 观察效果.
# 1. 定义方法, 用于关联子进程的.
def my_method():
    for i in range(10):
        print(f'工作中... {i}')
        time.sleep(0.3)  # 总休眠时间 = 0.3 * 10 = 3秒
​
​
# 2. 在main方法中测试.
if __name__ == '__main__':
    # 3. 创建子进程对象, 并启动.
    p1 = multiprocessing.Process(target=my_method)
    # 方式1: 设置子进程p1为守护进程, 
    # 非守护进程是: main进程, 所以: 当main进程关闭的时候, 它的守护进程也会关闭.
    # p1.daemon = True
    p1.start()  # 3秒后结束.
​
    # 4. 主进程执行1秒后结束.
    time.sleep(1)
​
    # 方式2: 手动关闭子进程.
    # 会导致子进程变成僵尸进程, 即: 不会立即释放资源.
    # 而是交由init进程接管(充当新的父进程), 在合适的时机释放资源.
    p1.terminate()      # 不推荐使用.
​
    # 5. 打印主进程的结束提示.
    print('主进程 main 执行结束!')

二. 线程

介绍

线程是CPU调度资源的最基本单位, 进程是CPU分配资源的基本单位.进程 = 可执行程序, 文件. 即: *.exe = 进程, 微信, QQ都是进程.线程 = 进程的执行路径, 执行单元. 微信这个进程, 可以实现: 和张三聊聊天, 和李四聊天, 查看朋友圈, 微信支付... 车在车道上跑, 有: 单行道, 双车道, 四车道, 八车道...

多线程实现

无论是进程, 还是线程, 都是实现 多任务的一种方式, 目的都是: 充分利用CPU资源, 提高效率.线程的操作步骤: 1. 导包. 2. 创建线程对象. 3. 启动线程.

代码
# 导包
import threading, time
​
# 1.定义函数, 表示: 敲代码.
def coding():
    for i in range(10):
        print(f"正在敲代码... {i}")
        time.sleep(0.1)
​
​
# 2.定义函数, 表示: 听音乐
def music():
    for i in range(10):
        print(f"正在听音乐... {i}")
        time.sleep(0.1)
​
# 在main中测试.
if __name__ == '__main__':
    # 3. 创建线程对象, 分别关联上述的两个函数.
    t1 = threading.Thread(target=coding)
    t2 = threading.Thread(target=music)
​
    # 4. 启动线程.
    t1.start()
    t2.start()

带有参数

Thread类(线程类)中的参数 和 Process类(进程类)的参数几乎一致: target: 关联目标函数的. name: 线程名(Thread-1, Thread-2...) 或者 进程名(Process-1, Process-2...). args: 以 元组的 形式传参, 个数及对应的类型都要一致. kwargs: 以 字典的 形式传参, 个数要一致.

# 导包
# from threading import Thread
import threading, time
​
​
# 1. 定义函数, 实现: 小明正在敲第n行代码.
def coding(name, num):
    for i in range(1, num + 1):
        print(f'{name} 正在敲第 {i} 行代码...')
        time.sleep(0.1)
​
​
# 2. 定义函数, 实现: 小明正在听第n首音乐
def music(name, count):
    for i in range(1, count + 1):
        print(f'{name} 正在听第 {i} 首音乐......')
        time.sleep(0.1)
​
​
# 在main中测试
if __name__ == '__main__':
    # 3. 创建线程对象.
    t1 = threading.Thread(name='杨过', target=coding, args=('乔峰', 10))
    t2 = threading.Thread(name='大雕', target=music, kwargs={'count': 10, 'name': '慕容复'})
    # print(f't1线程的名字: {t1.name}')
    # print(f't2线程的名字: {t2.name}')
​
    # 4. 启动线程.
    t1.start()
    t2.start()

线程注意事项

记忆:

  1. 多线程的执行具有 随机性(无序性), 其实就是在抢CPU的过程, 谁抢到, 谁执行.

  2. 默认情况下: 主线程会等待子线程执行结束再结束.

  3. 线程之间 会共享当前进程的 资源.

  4. 多线程环境 并发 操作共享资源, 有可能引发安全问题, 需要通过 线程同步(加锁) 的思想来解决.

关于CPU的资源分配, 调度, 思路主要有两种: 1. 均分时间片, 即: 每个进程(线程)占用CPU的时间都是 相等的. 2. 抢占式调度, 谁抢到, 谁执行. Python用的是这种.

无序

# 导包
import threading, time
​
​
# 需求: 创建多个线程, 多次运行, 观察歌词线程的执行顺序.
# 1. 定义函数, 获取线程, 并打印.
def get_info():
    # 休眠.
    time.sleep(0.5)
​
    # 获取当前的线程对象, 并打印.
    cur_thread = threading.current_thread()
    print(f'当前线程是: {cur_thread}')
​
​
​
# 在main中测试
if __name__ == '__main__':
    # 2. 创建多个线程对象.
    for i in range(10):
        th = threading.Thread(target=get_info)
        # 3. 启动线程即可.
        th.start()

(默认)主线程等待子线程结束

演示
import threading, time
​
​
# 1. 定义函数, 执行: 3秒.
def coding():
    for i in range(10):
        print(f'coding... {i}')
        time.sleep(0.3)     # 总休眠时间 = 0.3 * 10 = 3秒
​
​
# 在main中测试
if __name__ == '__main__':
    # 2. 创建线程对象.
    th = threading.Thread(target=coding)
​
    # 3. 启动线程.
    th.start()
​
    # 4. 设置主线程(main线程), 执行1秒就关闭.
    time.sleep(1)
​
    # 5. 提示即可.
    print('主线程(main)执行结束了!')
解决

设置子线程为守护线程

import threading, time
​
​
# 1. 定义函数, 执行: 3秒.
def coding():
    for i in range(10):
        print(f'coding... {i}')
        time.sleep(0.3)     # 总休眠时间 = 0.3 * 10 = 3秒
​
​
# 在main中测试
if __name__ == '__main__':
    # 2. 创建线程对象.
    # 方式1: 设置th线程为: 守护线程.
    # th = threading.Thread(target=coding, daemon=True) # 推荐使用.
​
    # 方式2: setDaemon()函数实现.
    th = threading.Thread(target=coding)
    th.setDaemon(True)  # 函数已过时, 推荐使用方式1.
​
    # 3. 启动线程.
    th.start()
​
    # 4. 设置主线程(main线程), 执行1秒就关闭.
    time.sleep(1)
​
    # 5. 提示即可.
    print('主线程(main)执行结束了!')

共享全局变量

代码
import threading
import time
​
my_list = []
print('main外输出')
​
​
def write_list():
    for i in range(10):
        my_list.append(i)
        print(f'{i} 已经添加到列表中')
    print('write_list:', my_list)
​
​
def read_list():
    time.sleep(1)
    print('read_list:', my_list)
​
​
if __name__ == '__main__':
    th1 = threading.Thread(target=write_list)
    th2 = threading.Thread(target=read_list)
    th1.start()
    th2.start()
执行结果

数据安全

多线程环境 并发 操作共享资源, 有可能引发安全问题, 需要通过 线程同步(加锁) 的思想来解决.参照4

演示:
# 导包
# 导包
import threading
import time
​
# 定义全局变量
num = 0
​
​
# 定义函数add1()累加
def add1():
    global num
    for i in range(1000000):
        num += 1
    print(f'add1: {num}')
​
​
# 定义函数add2()累加
def add2():
    global num
    for i in range(1000000):
        num += 1
    print(f'add2: {num}')
​
​
if __name__ == '__main__':
    # 创建线程对象
    th1 = threading.Thread(target=add1)
    th2 = threading.Thread(target=add2)
​
    # 启动线程
    th1.start()     # 19328862
    th2.start()     # 20000000

问题描述

两个线程分别对全局变量累加100W次, 正常应为100W 和 200W的结果,但是结果和想象不一样

原因

多线程环境 并发 操作共享资源, 引发安全问题

  • 正常情况:

  1. 假设 全局变量 global_num = 0

  2. 此时 线程t1抢到了资源, 执行累加(一次)动作, 累加后: global_num = 1

  3. 假设 线程t2抢到了资源, 执行累加(一次)动作, 累加后: global_num = 2

  • 非正常情况:

  1. 假设 全局变量 global_num = 0

  2. 此时 线程t1抢到了资源, 但是还没有来得及执行累加动作时, 被t2线程抢走了资源.

  3. 此时 线程t2读取到的 global_num = 0

  4. 此时就会出现 线程t1累加1次, global_num = 1, 线程t2累加1次, global_num = 1

  5. 综上所述, t1和t2线程一共累加了2次, 但是 global_num的值 只加了1

  6. 之所以会有这样的情况, 原因是: 1个线程在执行某1个完整动作期间, 可以被别的前程抢走资源, 就有可能引发安全问题.

解决方案

采用 线程同步 的思想, 即: 加锁

# 导包
import threading
import time
​
# 定义全局变量
num = 0
mutex = threading.Lock()
​
​
# 定义函数add1()累加
def add1():
    # 加锁
    mutex.acquire()
    global num
    for i in range(1000000):
        num += 1
    print(f'add1: {num}')
    # 释放锁
    mutex.release()
​
​
# 定义函数add2()累加
def add2():
    # 加锁
    mutex.acquire()
    global num
    for i in range(1000000):
        num += 1
    print(f'add2: {num}')
    # 释放锁
    mutex.release()
​
​
if __name__ == '__main__':
    # 创建线程对象
    th1 = threading.Thread(target=add1)
    th2 = threading.Thread(target=add2)
​
    # 启动线程
    th1.start()  # 19328862
    th2.start()  # 20000000

线程同步

用于解决多线程 并发 操作共享变量的安全问题的, 保证同一时刻只有1个线程操作共享变量.

锁的概述

互斥锁:

对共享数据进行锁定,保证同一时刻只有一个线程去操作。

注:

互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程进行等待,等锁使用完释放后,其它等待的线程再去抢这个锁。

使用步骤

  1. 创建锁

  2. 在合适的地方加锁

  3. 在合适的地方释放锁

代码
# 导包
import threading, time
​
# 1. 定义全局变量.
global_num = 0
​
# 创建锁.
mutex = threading.Lock()        # 互斥锁.
# mutex2 = threading.Lock()        # 互斥锁.
​
# 2. 定义函数1, 对全局变量 global_num累加100W次.
def get_sum1():
    # 加锁.
    mutex.acquire()
    # 声明变量为 全局变量.
    global global_num
    # 具体的累加动作.
    for i in range(1000000):
        global_num += 1
    # 解锁
    mutex.release()
    # 累加完毕后, 打印结果.
    print(f'get_sum1函数执行完毕, global_num = {global_num}')
​
​
# 3. 定义函数2, 对全局变量 global_num累加100W次.
def get_sum2():
    # 加锁.
    # mutex2.acquire()
    mutex.acquire()
    # 声明变量为 全局变量.
    global global_num
    # 具体的累加动作.
    for i in range(1000000):
        global_num += 1
    # 解锁
    # mutex2.release()
    mutex.release()
    # 累加完毕后, 打印结果.
    print(f'get_sum2函数执行完毕, global_num = {global_num}')
​
# main函数, 测试
if __name__ == '__main__':
    # 4. 创建线程对象.
    t1 = threading.Thread(target=get_sum1)
    t2 = threading.Thread(target=get_sum2)
​
    # 5. 启动线程.
    t1.start()
    t2.start()

注意事项

  1. 必须使用同一把锁, 否则可能锁不住(同一块cpu有两个锁两个门)

  2. 在合适的地方释放锁, 否则可能死锁

进程与线程的区别

关系

线程依附于进程, 没有进程就没有线程

一个进程默认提供一个线程, 可以创建多个进程

区别

  1. 进程间数据隔离

  2. 线程数据共享,但是要注意资源竞争 ,可以加互斥锁

  3. 进程比线程的资源开销大

  4. 线程是CPU调度资源的最基本单位, 进程是CPU分配资源的基本单位

  5. 线程不能独立执行, 必须存在于进程中

  6. python中 多进程单进程多线程 稳定

优缺点

  • 进程:

优点: 可以使用多核

缺点: 资源开销大

  • 线程:

优点: 资源开销小

缺点: 不可以使用多核

总结

线程依赖于进程

进程数据隔离, 线程数据共享

进程资源开销比线程资源开销大, 所以相对更稳定

无论多线程还是多进程都可以实现多任务, 目的都是: 充分利用cpu资源, 提高程序的执行效率

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

unreal engine 5.4.4 runtime 使用PCG

Unreal PCG Runtime runtime环境下控制PCG PCG Graph 这里简单的在landscape上Spawn Static Mesh 和 Spawn Actor GraphSetting 自定义的参数,方便修改 场景 这里新建了一个蓝图Actor PCG_Ctrl, 用来runtime的时候控制PCG生成 Construct 获取场景中的PCGVolum…

开源还是封闭?人工智能的两难选择

这篇文章于 2024 年 7 月 29 日首次出现在 The New Stack 上。人工智能正处于软件行业的完美风暴中,现在马克扎克伯格 (Mark Zuckerberg) 正在呼吁开源 AI。 关于如何控制 AI 的三个强大观点正在发生碰撞: 1 . 所有 AI 都应该是开…

易保全出席人工智能应用场景高峰论坛,发布AI-数据资产管理平台2.0应用成果

2024年9月5日,由上海合作组织国家多功能经贸平台、重庆市科技发展基金会指导,重庆市渝中区商务委员会等相关部门主办、华智未来(重庆)科技有限公司承办,重庆民营经济国际合作商会协办的“智驭未来创想无界人工智能应用场景高峰论坛暨成果发布…

区块链-P2P(八)

前言 P2P网络(Peer-to-Peer Network)是一种点对点的网络结构,它没有中心化的服务器或者管理者,所有节点都是平等的。在P2P网络中,每个节点都可以既是客户端也是服务端,这种网络结构的优点是去中心化、可扩展…

linux(ubuntu)安装QT-ros插件

Linux下的qt安装ros插件 查看qt版本和对应的ros插件版本查看qt版本查看 qt creator 版本 qt creator进行更新升级下载版本对应的ros_qtc_plugin 插件插件安装安装成功 查看qt版本和对应的ros插件版本 想要qt与ros联合开发,我门需要在qt creator中添加ros的插件&…

Docker Hub 仓库国内无法拉取镜像,如何应对?

Docker Hub 仓库国内无法拉取镜像,如何应对? 描述:早上起来发现交流群中有童鞋在说无法在Docker Hub中正常拉取镜像,然后在公司的服务器上拉取最新的nginx:latest镜像发现确实无法拉取。 注册一个阿里云账户 由于前面作者发布过两篇同步国外…

Android平台RTSP|RTMP播放器(SmartPlayer)集成必读

技术背景 好多开发者拿到大牛直播SDK的Android平台RTSP、RTMP播放模块,基本上不看说明,测试后,就直接集成到自己系统了。不得不说,我们的模块虽然接口很多,功能支持全面,但是上层的demo设计逻辑确实简单&a…

安装Android Studio及第一个Android工程可能遇到的问题,gradle下载过慢、sync失败?

Android Studio版本众多,电脑操作系统、电脑型号、电脑硬件也是多种多样,幸运的半个小时内可以完成安装,碰到不兼容的电脑,一天甚至更长时间都无法安装成功。 Android安装及第一个Android工程分为4个步骤,为什么放到一…

echarts进度

echarts图表集 const data[{ value: 10.09,name:制梁进度, color: #86C58C,state: }, { value: 66.00,name:架梁进, color: #C6A381 ,state:正常}, { value: 33.07,name:下部进度, color: #669BDA,state:正常 }, ];// const textStyle { "color": "#CED6C8&…

【whisper】使用whisper实现语音转文字

whisper需要ffmpeg支持 官网下载ffmpeg https://www.gyan.dev/ffmpeg/builds/下载完毕后解压放到合适的位置 添加环境变量 在cmd中输入以下 ffmpeg -version出现下面结果代表成功 安装whisper pip install openai-whisper在vscode中运行 测试代码 import whisperif __n…

SAP 生产订单工序删除状态撤回简介

SAP 生产订单工序删除状态撤回简介 一、业务场景二、处理办法三、系统控制一、业务场景 生产订单正常没有按工序分配物料,系统会自动会把物料分配到第一道工序中 生产订单中的0010工序中对应的组件的栏位被标识,表示有物料分配到了0010的工序中,正常情况下0010的工序被分配…

软件测试基础面试题11问(带答案)

1、编写测试用例有哪些? 答:等价类、边界值、错误推测法、场景法,我个人常用的方法就是这些 2、Beta测试与Alpha测试的区别? 答:两者的主要区别是测试的场所不同。Alpha测试是指把用户请到开发方的场所来测试,beta测试…

Tomato靶场渗透测试

1.扫描靶机地址 可以使用nmap进行扫描 由于我这已经知道靶机地址 这里就不扫描了 2.打开网站 3.进行目录扫描 dirb http://172.16.1.113 发现有一个antibot_image目录 4.访问这个目录 可以看到有一个info.php 5.查看页面源代码 可以发现可以进行get传参 6.…

C#文件的输入和输出

一个文件是一个存储在磁盘中带有指定名称和目录路径的数据集合.当打开文件进行读写时,它变成一个流.从根本上说,流是通过通信路径传递的字节序列.有两个主要的流:输入流和输出流.输入流用于从文件读取数据,输出流用于向文件写入数据. C#I/O类 System.IO命名空间有各种不同的类…

Unity Xcode方式接入sdk

入口 创建 GameAppController 类 继承 UnityAppController 并且在类的实现之前 需要 加 IMPL_APP_CONTROLLER_SUBCLASS(GameAppController),表明这个是程序的入口。UnityAppController 实现了 UIApplicationDelegate。 可以简单看下 UIApplicationDelegate 的生命周…

拍卖新纪元:Spring Boot赋能在线拍卖解决方案

需求分析 1.1技术可行性:技术背景 在线拍卖系统是在Windows操作系统中进行开发运用的,而且目前PC机的各项性能已经可以胜任普通网站的web服务器。系统开发所使用的技术也都是自身所具有的,也是当下广泛应用的技术之一。 系统的开发环境和配置…

docker实战基础一 (Docker基础命令)

一、docker安装 # step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # Step 3: 更新并安装 Doc…

Spring Boot:医疗排班系统开发的技术革新

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…

windows安装php7.4

windows安装php7.4 1.通过官网下载所需的php版本 首先从PHP官网(https://www.php.net/downloads.php)或者Windows下的PHP官网(http://windows.php.net/download/)下载Windows版本的PHP安装包。下载后解压到一个路径下。 2.配…

从PDF到CAD:四大必备转换工具推荐!

无论是建筑设计师还是机械工程师,都面临着将旧图纸或扫描件转换成可编辑CAD文件的任务。这不仅是为了提高工作效率,更是为了适应数字化转型的大趋势。今天,我们就来探索几款高效且用户友好的解决方案! 福昕PDF转换大师&#xff0…