subprocess—Python多进程模块
subprocess—Python多进程模块
1.概述
这篇文章介绍并行运算中的subprocess模块,subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。
subprocess
它可以用来调用第三方工具(例如:exe、另一个python文件、命令行工具)
subprocess
模块首先推荐使用的是它的 run 方法,更高级的用法可以直接使用 Popen 接口
subprocess
模块提供了了三个 API 处理进程。Python 3.5 中添加的 run() 函数,是一个运行进程高级 API,也可以收集它的输出。call(),check_call() 以及 check_output() 是从 Python2 继承的较早的高级 API。在已存的程序中,它们仍然被广泛支持和使用。
类 Popen 是一个低级 API,用于构建其他的 API 以及用于更复杂的进程交互。Popen 构造函数接受参数设置新进程,以便父进程可以通过管道与它通信。它提供了它替代的模块和函数的所有功能,以及更多功能。
API 对于所有使用场景是一致的,许多之前必要的额外步骤(例如关闭额外的文件描述符并确保通道关闭)现在已经内置了,无需单独代码处理。
subprocess
模块的目的在于替换几个旧的模块和方法,替换 os.system(),os.spawnv() , os 和 popen2 模块中 popen 的变体以及 commands() 模块等。为了更容易地将 subprocess 同其他模块比较,本节中的许多示例都重新创建了用于 os 和 popen2 中的。
2.subprocess模块
2.1.运行外部命令run
1.默认运行外部命令
调用run方法创建一个进程执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
例如linux的ls
命令
run方法参数
- args:表示要执行的命令。必须是一个字符串,字符串参数列表。
- stdin、stdout 和 stderr:子进程的标准输入、输出和错误。其值可以是 subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 None。subprocess.PIPE 表示为子进程创建新的管道。subprocess.DEVNULL 表示使用 os.devnull。默认使用的是 None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。
- timeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并弹出 TimeoutExpired 异常。
- check:如果该参数设置为 True,并且进程退出状态码不是 0,则弹 出 CalledProcessError 异常。
- encoding: 如果指定了该参数,则 stdin、stdout 和 stderr 可以接收字符串数据,并以该编码方式编码。否则只接收 bytes 类型的数据。
- shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
import subprocess
# 第一个参数是要运行的ls命令,第二个参数是ls命令的参数
completed = subprocess.run(['ls', '-l'])
print('returncode:', completed.returncode)
命令行参数被作为一个字符串列表传入,这样能够避免转义引号以及其他会被 shell 解析的特殊字符。run() 方法返回一个 CompletedProcess 实例,包含进程退出码以及输出等信息。
total 16
-rw-r--r-- 1 edy staff 103 Feb 17 14:01 __init__.py
-rw-r--r-- 1 edy staff 207 Feb 17 15:46 my_subprocess_1.py
returncode: 0
2.新开一个shell进程运行外部命令
设置 shell 参数为 True 会导致 subprocess 创建一个新的中间 shell 进程运行命令。默认的行为是直接运行命令。
import subprocesscompleted = subprocess.run('echo $HOME', shell=True)
print('returncode:', completed.returncode)
使用中间 shell 意味着在运行该命令之前处理命令字符串的变量,glob 模式以及其他特殊的 shell 功能。
/Users/edy
returncode: 0
提醒
使用 run() 而没有传递 check=True 等价于调用 call(),它仅仅返回进程的退出码。
2.2.错误处理
CompletedProcess 的 returncode 属性是程序的退出码。调用者负责解释它并检测错误。如果 run() 的 check 参数是 True,退出码将会被检查,如果有错误出现将会引发 CalledProcessError 异常。
import subprocesstry:subprocess.run(['false'], check=True)
except subprocess.CalledProcessError as err:print('ERROR:', err)
false 命令总是返回非零状态码,run() 将它解释为一个错误。
ERROR: Command '['false']' returned non-zero exit status 1
提醒
给 run() 方法传递 check=True 等价于调用 check_all()。
2.3.捕获输出
由 run() 启动的进程的标准输入输出渠道绑定在了父进程上。那就意味着调用程序不能捕获命令的输出。给 stdout 和 stderr 参数传递 PIPE 可以捕获输出用于后续处理。
import subprocess# 执行后的结果赋值给管道
completed = subprocess.run(['ls', '-1'],stdout=subprocess.PIPE,
)
# 通过管道输出命令执行结果
print('returncode:', completed.returncode)
print('Have {} bytes in stdout:\n{}'.format(len(completed.stdout),completed.stdout.decode('utf-8'))
)
ls -1 命令成功运行了,所以它打印到标准输出的文本被捕获并返回了
returncode: 0
Have 31 bytes in stdout:
__init__.py
my_subprocess_1.py
提醒
传入 check=True 以及设置 stdout 为 PIPE 等价于使用 check_output()。
2.4.捕获错误输出
下个例子在子 shell 中运行了一些列的命令。在命令出错退出之前消息被发送到了标准输出和错误输出。
import subprocesstry:completed = subprocess.run('echo to stdout; echo to stderr 1>&2; exit 1',check=True,shell=True,stdout=subprocess.PIPE,)
except subprocess.CalledProcessError as err:print('ERROR:', err)
else:print('returncode:', completed.returncode)print('Have {} bytes in stdout: {!r}'.format(len(completed.stdout),completed.stdout.decode('utf-8')))
标准错误输出被打印到了控制台,但是标准错误输出被隐藏了
to stderr
ERROR: Command 'echo to stdout; echo to stderr 1>&2; exit 1'
returned non-zero exit status 1
为了阻止 run() 运行命令产生的错误消息打印到控制台,设置 stderr 参数为常量 PIPE。
import subprocesstry:completed = subprocess.run('echo to stdout; echo to stderr 1>&2; exit 1',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,)
except subprocess.CalledProcessError as err:print('ERROR:', err)
else:print('returncode:', completed.returncode)print('Have {} bytes in stdout: {!r}'.format(len(completed.stdout),completed.stdout.decode('utf-8')))print('Have {} bytes in stderr: {!r}'.format(len(completed.stderr),completed.stderr.decode('utf-8')))
这个例子没有设置 check=True,所以命令的输出被捕获并且打印。
returncode: 1
Have 10 bytes in stdout: 'to stdout\n'
Have 10 bytes in stderr: 'to stderr\n'
为了捕获当使用 check_output() 产生的错误消息时,设置 stderr 为 STDOUT,并且这些消息将与该命令的其余输出合并。
import subprocesstry:output = subprocess.check_output('echo to stdout; echo to stderr 1>&2',shell=True,stderr=subprocess.STDOUT,)
except subprocess.CalledProcessError as err:print('ERROR:', err)
else:print('Have {} bytes in output: {!r}'.format(len(output),output.decode('utf-8')))
输出顺序可能会变化,取决于对标准输出流的缓冲方式以及打印的数据量。
Have 20 bytes in output: 'to stdout\nto stderr\n'
2.5.抑制输出
某些情况下,输出不应该被展示和捕获,使用 DEVNULL 抑制输出流。这个例子抑制了标准输出流和错误输出流。
import subprocesstry:completed = subprocess.run('echo to stdout; echo to stderr 1>&2; exit 1',shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,)
except subprocess.CalledProcessError as err:print('ERROR:', err)
else:print('returncode:', completed.returncode)print('stdout is {!r}'.format(completed.stdout))print('stderr is {!r}'.format(completed.stderr))
DEVNULL 的名字来自于 Unix 特殊的设备文件,/dev/null,当读时直接响应文件结束,写时接收但忽略任何数量的输入。
returncode: 1
stdout is None
stderr is None
3.直接使用管道Popen
基础的操作使用run()
函数都能完成,如果遇到复杂的操作就需要使用更高级的 Popen类提供的方法, 能够对如何运行命令以及如何处理输入输出流提供更多的控制。例如,通过对 stdin,stdout 以及 stderr 传递不同的参数,可以达到模仿 os.popen() 的效果。
Popen类构造器参数
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。
0:不使用缓冲区
1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式
正数:表示缓冲区大小
负数:表示使用系统默认的缓冲区大小。 - stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
- cwd:用于设置子进程的当前目录。
- env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。
Popen 对象方法
- poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。
- wait(timeout): 等待子进程终止。
- communicate(input,timeout): 和子进程交互,发送和读取数据。
- send_signal(singnal): 发送信号到子进程 。
- terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。
- kill(): 杀死子进程。发送 SIGKILL 信号到子进程。
3.1.与进程单向通信
运行一个进程以及读取所有输出,设置 stdout 的值为 PIPE 并且调用 communicate()。
import subprocessprint('read:')
proc = subprocess.Popen(['echo', '"to stdout"'],stdout=subprocess.PIPE,
)
stdout_value = proc.communicate()[0].decode('utf-8')
print('stdout:', repr(stdout_value))
这个类似于 popen() 的工作方式,除了读取由 Popen 实例内部管理。
read:
stdout: '"to stdout"\n'
为了设置一个管道允许调用者向其写入数据,设置 stdin 为 PIPE
import subprocessprint('write:')
proc = subprocess.Popen(['cat', '-'],stdin=subprocess.PIPE,
)
proc.communicate('stdin: to stdin\n'.encode('utf-8'))
为了发送数据到进程的标准输入,请使用 communicate(),这就有点同 w 模式的 popen 了。
write:
stdin: to stdin
3.2.与进程双向通信
为了设置 Popen 实例同时进行读写,结合之前使用过的技术。
import subprocessprint('popen2:')proc = subprocess.Popen(['cat', '-'],# 输入和输出设置为管道,进行通信stdin=subprocess.PIPE,stdout=subprocess.PIPE,
)
msg = 'through stdin to stdout'.encode('utf-8')
stdout_value = proc.communicate(msg)[0].decode('utf-8')
print('pass through:', repr(stdout_value))
这样设置使用就有点像 popen2() 了。
popen2:
pass through: 'through stdin to stdout'
3.3.捕获错误输出
同时查看 stdout 和 stderr 输出流也是可能的,就像 popen3()。
import subprocessprint('popen3:')
proc = subprocess.Popen('cat -; echo "to stderr" 1>&2',shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
)
msg = 'through stdin to stdout'.encode('utf-8')
stdout_value, stderr_value = proc.communicate(msg)
print('pass through:', repr(stdout_value.decode('utf-8')))
print('stderr :', repr(stderr_value.decode('utf-8')))
从 stderr 中读取错误输出类似于 stdout 。传入 PIPE 告诉 Popen 附加到通道,并且使用 communicate() 在返回之前读取所有数据。
popen3:
pass through: 'through stdin to stdout'
stderr : 'to stderr\n'
3.4.合并常规和错误输出
为了将进程的错误输出导向标准输出渠道,设置 stderr 为 STDOUT 而不是 PIPE。
import subprocessprint('popen4:')
proc = subprocess.Popen('cat -; echo "to stderr" 1>&2',shell=True,# 输入管道stdin=subprocess.PIPE,# 输出管道stdout=subprocess.PIPE,# 错误输出stderr=subprocess.STDOUT,
)
msg = 'through stdin to stdout\n'.encode('utf-8')
stdout_value, stderr_value = proc.communicate(msg)
print('combined output:', repr(stdout_value.decode('utf-8')))
print('stderr value :', repr(stderr_value))
这种合并输出的方式类似于 popen4() 的工作方式。
popen4:
combined output: 'through stdin to stdout\nto stderr\n'
stderr value : None
3.5.连接管道的段
多个命令可以被连接到一个 管道 中,类似于 Unix shell 的工作方式,实现这种操作,可以通过创建分隔的 Popen 实例并将他们的输入输出链在一起。
一个 Popen 实例的 stdout
属性被用作下一个的 stdin
参数,而不是之前的常量 PIPE
。要获取整个执行的输出,可以从最后一个 Popen 实例的 stdout 流读取。
import subprocesscat = subprocess.Popen(['cat', 'index.rst'],stdout=subprocess.PIPE,
)# 把cat拼接到grep
grep = subprocess.Popen(['grep', '.. literalinclude::'],# 输入为上个命令的输出值stdin=cat.stdout,stdout=subprocess.PIPE,
)# grep拼接到cut
cut = subprocess.Popen(['cut', '-f', '3', '-d:'],stdin=grep.stdout,stdout=subprocess.PIPE,
)end_of_pipe = cut.stdoutprint('Included files:')
for line in end_of_pipe:print(line.decode('utf-8').strip())
这个例子同下面的命令行操作:
cat index.rst | grep ".. literalinclude" | cut -f 3 -d:
这个部分首先管道读取 reStructuredText 源文件,然后找到所有包含其他文件的行,最后打印被包含的文件名称
Included files:
subprocess_os_system.py
subprocess_shell_variables.py
subprocess_run_check.py
subprocess_run_output.py
subprocess_run_output_error.py
subprocess_run_output_error_trap.py
subprocess_check_output_error_trap_output.py
subprocess_run_output_error_suppress.py
subprocess_popen_read.py
subprocess_popen_write.py
subprocess_popen2.py
subprocess_popen3.py
subprocess_popen4.py
subprocess_pipes.py
repeater.py
interaction.py
signal_child.py
signal_parent.py
subprocess_signal_parent_shell.py
subprocess_signal_setpgrp.py
3.6.同另一个命令交互
所有前面的例子都假定了一个有限的交互,communicate() 方法读取所有输出并等待子进程在返回之前退出。在程序运行时也可以逐步写入和读取 Popen 实例使用的单个管道句柄。从标准输入中读取并希望如标准输出的简单回声程序说明了这种技术。
脚本 repeater.py 被用作下一个例子的子进程。它从 stdin 读取并且写入到 stdout ,一次一行,直到再没有输入。当开始和停止的时候,它也往 stderr 写入了一条消息,展示子进程的声明周期。
创建repeater.py文件,复制下面的代码。
import syssys.stderr.write('repeater.py: starting\n')
sys.stderr.flush()while True:next_line = sys.stdin.readline()sys.stderr.flush()if not next_line:breaksys.stdout.write(next_line)sys.stdout.flush()sys.stderr.write('repeater.py: exiting\n')
sys.stderr.flush()
下一个例子中以不同的方式使用 Popen 实例的 stdin 和 stdout 文件句柄。在第一个例子中,五个数字被依次写入到进程的 stdin,每次写入后,紧接着会读出输入并打印出来了。第二个例子中相同的五个数字被写入,但是输出通过 communicate() 依次行读取了。
import io
import subprocessprint('One line at a time:')
proc = subprocess.Popen('python3 repeater.py',shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,
)
stdin = io.TextIOWrapper(proc.stdin,encoding='utf-8',line_buffering=True, # send data on newline
)
stdout = io.TextIOWrapper(proc.stdout,encoding='utf-8',
)
for i in range(5):line = '{}\n'.format(i)stdin.write(line)output = stdout.readline()print(output.rstrip())
remainder = proc.communicate()[0].decode('utf-8')
print(remainder)print()
print('All output at once:')
proc = subprocess.Popen('python3 repeater.py',shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,
)
stdin = io.TextIOWrapper(proc.stdin,encoding='utf-8',
)
for i in range(5):line = '{}\n'.format(i)stdin.write(line)
stdin.flush()output = proc.communicate()[0].decode('utf-8')
print(output)
每个循环中, “repeater.py: exiting” 行在输出的不同点出现。
One line at a time:
repeater.py: starting
0
1
2
3
4
repeater.py: exitingAll output at once:
repeater.py: starting
repeater.py: exiting
0
1
2
3
4
3.7.进程间的信号
os 模块的进程管理示例包括使了用 os.fork() 和 os.kill() 进程之间的信号演示。由于每个 Popen 实例都提供了一个 pid 属性和子进程 id,所以可以对子进程执行类似的操作。下一个例子合并了两个脚本,子进程设置了一个 USR 信号处理器。
脚本文件内容signal_child.py
import os
import signal
import time
import syspid = os.getpid()
received = Falsedef signal_usr1(signum, frame):"Callback invoked when a signal is received"global receivedreceived = Trueprint('CHILD {:>6}: Received USR1'.format(pid))sys.stdout.flush()print('CHILD {:>6}: Setting up signal handler'.format(pid))
sys.stdout.flush()
signal.signal(signal.SIGUSR1, signal_usr1)
print('CHILD {:>6}: Pausing to wait for signal'.format(pid))
sys.stdout.flush()
time.sleep(3)if not received:print('CHILD {:>6}: Never received signal'.format(pid))
这个脚本被当做父进程运行,它启动了 signal_child.py,然后发送了 USR1 信号。
import os
import signal
import subprocess
import time
import sysproc = subprocess.Popen(['python3', 'signal_child.py'])
print('PARENT : Pausing before sending signal...')
sys.stdout.flush()
time.sleep(1)
print('PARENT : Signaling child')
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
运行结果
PARENT : Pausing before sending signal...
CHILD 26976: Setting up signal handler
CHILD 26976: Pausing to wait for signal
PARENT : Signaling child
CHILD 26976: Received USR1
3.8.进程 组 / 会话
如果由 Popen 创建的进程产生子进程,那么子进程将不会收到任何发送给父进程的任何信号。这意味着当对 Popen 使用 shell 参数时,很难通过发送 SIGINT 和 SIGTERM 来使 shell 中启动的命令终止。
subprocess_signal_parent_shell.py
import os
import signal
import subprocess
import tempfile
import time
import sysscript = '''#!/bin/sh
echo "Shell script in process $$"
set -x
python3 signal_child.py
'''
script_file = tempfile.NamedTemporaryFile('wt')
script_file.write(script)
script_file.flush()proc = subprocess.Popen(['sh', script_file.name])
print('PARENT : Pausing before signaling {}...'.format(proc.pid))
sys.stdout.flush()
time.sleep(1)
print('PARENT : Signaling child {}'.format(proc.pid))
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
time.sleep(3)
用于发送信号的 pid 与等待信号的运行 shell 脚本的子进程 id 不同,因为这个例子中有三个独立的进程在交互:
1.主程序 subprocess_signal_parent_shell.py
2.主程序创建的运行脚本的 shell 进程。
3.程序 signal_child.py
PARENT : Pausing before signaling 26984...
Shell script in process 26984
+ python3 signal_child.py
CHILD 26985: Setting up signal handler
CHILD 26985: Pausing to wait for signal
PARENT : Signaling child 26984
CHILD 26985: Never received signal
要在不知道进程 id 的情况下向后代进程发送信号,请使用进程组关联这些子进程,以便可以一起发送信号。进程组使用 os.setpgrp() 创建,它将进程组 id 设置为当前进程 id。所有子进程都从父进程继承他们的进程组,因为它只应在由 Popen 及其后代创建的 shell 中设置,所以不应在创建 Popen 的相同进程中调用 os.setpgrp() 。而是,应在作为 Popen 的 preexec_fn 参数设置的函数中调用,它会在新进程的 fork 之后运行,在用 exec 运行 shell 之前。为了给进程组发送信号,应该使用 os.killpg() 并使用 Popen 实例的进程 id。
subprocess_signal_setpgrp.py
import os
import signal
import subprocess
import tempfile
import time
import sysdef show_setting_prgrp():print('Calling os.setpgrp() from {}'.format(os.getpid()))os.setpgrp()print('Process group is now {}'.format(os.getpgrp()))sys.stdout.flush()script = '''#!/bin/sh
echo "Shell script in process $$"
set -x
python3 signal_child.py
'''
script_file = tempfile.NamedTemporaryFile('wt')
script_file.write(script)
script_file.flush()proc = subprocess.Popen(['sh', script_file.name],preexec_fn=show_setting_prgrp,
)
print('PARENT : Pausing before signaling {}...'.format(proc.pid))
sys.stdout.flush()
time.sleep(1)
print('PARENT : Signaling process group {}'.format(proc.pid))
sys.stdout.flush()
os.killpg(proc.pid, signal.SIGUSR1)
time.sleep(3)
整个运行流程如下
1.父进程实例化 Popen;
2.Popen 实例 fork 新进程;
3.新进程运行 os.setpgrp();
4.新进程运行 exec() 启动 shell;
5.shell 运行脚本;
6.shell 脚本再次 fork,然后启动 Python 解释器;
7.Python 运行 signal_child.py.
8.父进程发送信号非进程组,使用 Popen 实例的进程 id;
9.shell and Python 程序收到信号;
10.shell 忽略掉了信号。
11.运行 signal_child.py 的 Python 程序 调用了信号处理器。
python3 subprocess_signal_setpgrp.pyCalling os.setpgrp() from 75636
Process group is now 75636
PARENT : Pausing before signaling 75636...
Shell script in process 75636
+ python3 signal_child.py
CHILD 75637: Setting up signal handler
CHILD 75637: Pausing to wait for signal
PARENT : Signaling process group 75636
CHILD 75637: Received USR1
相关文章:
subprocess—Python多进程模块
subprocess—Python多进程模块 1.概述 这篇文章介绍并行运算中的subprocess模块,subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。 subprocess 它可以用来调用第三方工具(例如&#x…...
【APP渗透测试】 Android APP渗透测试技术实施以及工具使用(客户端服务端)
文章目录前言一、安全威胁分析二、主要风险项三、Android测试思维导图四、反编译工具五、Android客户端漏洞一、Jnaus漏洞漏洞二、数据备份配置风险漏洞漏洞三、Activity组件泄露漏洞漏洞四、BroadcastReceiver组件泄露漏洞漏洞五、允许模拟器Root环境登录漏洞漏洞六、未识别代…...
字符串匹配 - Overview
字符串匹配(String Matchiing)也称字符串搜索(String Searching)是字符串算法中重要的一种,是指从一个大字符串或文本中找到模式串出现的位置。字符串匹配概念字符串匹配问题的形式定义:文本(Text)是一个长度为 n 的数组 T[1..n]&…...
【IP课堂】Ip地址如何进行精准定位?
通过Ip地址定位,是目前网络上最常见的定位方式。当然,也是最简单的定位方式。其实方法大多都是雷同的,通过Ip定位,就目前网上公开的技术。如通过搜索关键词“定位,定位查询,Ip定位”等,只能查询…...
MySQL 临时表相关参数说明区别
MySQL 临时表参数innodb_temp_tablespaces_dir、innodb_temp_data_file_path、innodb_tmpdir、tmpdir 区分 解决方案 innodb_tmpdir: alter table生成中间表文件,innodb_tmpdir有指定效路径,优选选择innodb_tmpdir,没有则选择tm…...
第二章 变量和基本类型
1.string类型数据的另一种初始化方式 语法: string 变量名 (" 初始化内容 "); 2.C中的列表初始化 语法: 数据类型 变量名 { 变量初始化的值 } ; 数据类型 变量名 { 变量初始化的值 } ; 例: 3.引用常量 常量引…...
【Python】循环语句(while,for)、运算符、字符串格式化
一、while循环Python 编程中 while 语句用于循环执行程序,即在某条件下,循环执行某段程序,以处理需要重复处理的相同任务。其基本形式为:while 判断条件(condition):执行语句(statements)执行语句可以是单个语句或语句…...
利用设计模式、反射写代码
软件工程师和码农最大的区别就是平时写代码时习惯问题,码农很喜欢写重复代码而软件工程师会利用各种技巧去干掉重复的冗余代码。 业务同学抱怨业务开发没有技术含量,用不到设计模式、Java 高级特性、OOP,平时写代码都在堆 CRUD,个…...
Spring Cloud Alibaba--seata微服务详解之分布式事务(三)
上篇讲述gateway的部署和使用,gateway统一管理和转发了HTTP请求,在互联网中大型项目一定存在复杂的业务关系,尤其在商城类软件中如淘宝、PDD等商城,尤其在秒杀场景中,并发量可以到达千万级别,此时数据库就会…...
[USACO2023-JAN-Bronze] T3 Moo Operations 题解
一、题目描述因为Bessie觉得玩平时经常玩的只包含C O和W的字符串无聊了,Farmer John 给了她Q个新的字符串(1≤Q≤100),这Q个字符串只包含M和O。很明显,只包含M和O的单词里Bessie最喜欢的是”MOO”,所以她希望按照下面两个规则&…...
OKCC呼叫中心支持哪些接入方式?
使用OKCC系统开展呼叫中心业务,要将电话打通,需要什么样的设备接入到OKCC系统呢? 目前实际广泛使用的接入方式,既有硬件网关接入方式,也有软件接入方式,在生产实践中,我们须根据实际的需求及使…...
如何让手机共享电脑代理网络的WIFI热点
参考: 手机共享电脑的proxy网络 把电脑的网络代理给安卓设备如何将电脑的代理网络以WIFI热点的方式共享 电脑端设置代理: 打开电脑上的 proxy软件并设置其端口号(例如:7890),且允许局域网(例如…...
渲染有问题?怎么办?6种方法让你渲染无忧
简单点,解决问题的方式简单点。 日常工作中我们总会遇到各种各样的问题,比如渲不出图,速度太慢或效率太低,各种噪点和黑图等等,烦不胜烦,今天我就针对6个常见的问题给大家说下方法,一家之言仅供…...
中国人寿业务稳定性保障:“1+1+N” 落地生产全链路压测
引言 保险业务的数字化转型正如火如荼地进行,产品线上化、投保线上化、承保线上化、核保线上化等业务转型,导致系统的应用范围不断扩大,用户的高频访问也正在成为常态。同时,系统复杂性也呈指数上升,这些因素都增加了…...
2/17考试总结
时间安排 7:40–7:50 读题,T1 貌似是签到,T2,T4 DP,T3看起来很不可做。 7:50–8:00 T1,差分一下然后模拟就行了。 8:00–10:20 T2,注意到值域很小,可以考虑状压,想到一个状压状态数较少的 dp ,然后挂得彻底。发现有一…...
零信任-360连接云介绍(9)
360零信任介绍 360零信任又称360连接云安全访问平台(下文简称为:360连接云),360连接云,是360基于零信任安全理念,以身份为基础、动态访问控制为核心打造的安全访问平台。 通过收缩业务暴露面、自适应增强身份认证、终端持续检…...
使用dlib进行人脸检测和对齐
最近在配置人脸属性识别的服务,用过faceboxes_detector(faster rcnn的包),也用过face_recognition的,但是她们都没有做人脸对齐,而且检测人脸的范围也不太一样。没有做人脸对齐的时候,使用属性识…...
将python代码封装成c版本的dll动态链接库
前言 将python程序打包成DLL文件,然后用C调用生成的DLL文件,这是一种用C调用python的方法,这一块比较容易遇到坑。网上关于这一块的教程不是很多,而且大部分都不能完全解决问题。我在傻傻挣扎了几天之后,终于试出了一个…...
AI技术网关如何用于安全生产监测?有什么优势?
现代工业生产和运营的规模越来越庞大、系统和结构越来越复杂,现场的风险点多面广,给作业一线的安全监管带来极大的挑战。 针对工地、煤矿、危化品、加油站、烟花爆竹、电力等行业的安全生产监管场景,可以借助AI智能与物联网技术,…...
刷题记录:牛客NC53370 Forsaken的三维数点
传送门:牛客 题目描述: Forsaken现在在一个三维空间中,空间中每个点都可以用(x,y,z)表示。突然,三维空间的主人出现 了,如果Forsaken想要继续在三维空间中呆下去,他就必须回答三维空间主人的问题.主人会在空间 中坐标为(x,y,z)处…...
lombok的原理 和 使用
原理Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率。其实并没有改变字节码文件的任何内容,只是简化的程序员编写代码的方式。不使用lombok:使用lombok:lombok常用注解Setter :注解在类或字段&#x…...
UDP网络编程
UDP和TCP 前几节我们提到了计算机网络编程中的TCP编程,TCP和UDP都是计算机机网络通信的传输层中的传输协议,今天我们来学习计算机网络编程中的基于UDP传输协议的网络编程 首先我们要了解TCP和UDP的区别 它们是同属于计算机网络传输层的传输协议 TCP&…...
“合并区间”问题解析及其思考
合并区间题目以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。解析本题思路相对比较容易想先对各个区间按左…...
2023年理想新能源汽车核心部件解密
理想主要硬件清单(L9车型) 汽车结构 设置名称 规格 备注 价格 供应商 感知层...
C++ 将一个vector内容赋值给另一个vector,及swap与assign的区别
在本文中,我们将主要介绍5种将一个vector内容赋值给另一个vector的方式,顺便讨论下swap与assign的区别。 赋值 方式一、申明时赋值 vector<int> v2; v2.push_back(0); v2.push_back(1);vector<int> v1(v2); //声明方式二、使用assign赋值…...
PMP的价值有哪些?
我个人认为,考证只有两个出发点是正确的。一是为了提升自己或者满足自己的兴趣,另一个是和自己的职业规划相关。 比如,有同学想提升自己英语能力,可以考四六级,或者更厉害一点的考雅思、托福。比如,有的同…...
OnGUI label 控件||Unity 3D GUI教程||OnGUI Background Color 控件
Unity 3D Label 控件用于在设备的屏幕上创建文本标签和纹理标签,和Box 控件类似,可以显示文本内容或图片。Label 控件一般用于显示提示性的信息,如当前窗口的名称、游戏中游戏对象的名字、游戏对玩家的任务提示和功能介绍等。具体使用方法如下…...
从 JavaScript 中的数组中删除空对象
从数组中删除空对象: 使用 Array.filter() 方法遍历数组。将每个对象传递给 Object.keys() 方法并检查键的长度是否不等于 0。filter 方法将返回一个不包含空对象的新数组。 const arr [{}, {id: 1}, {}, {id: 2}, {}];const results arr.filter(element > {…...
【C++】AVL树和红黑树(插入和测试详解)
文章目录1、AVL树1.1 AVL树的插入1.2 总结与测试AVL树2、红黑树2.1 红黑树的插入2.2 红黑树的测试了解AVL树是为了了解红黑树,了解红黑树是为了更好的理解set和map。 1、AVL树 AVL树是在二叉搜索树的基础上进行了严格的平衡,能做到平衡的关键是通过平衡…...
昆明网站排名优化公司/廊坊seo关键词优化
6.引用 1.什么是引用? 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。 **类型& 引用变量名(对象名) 引用实体;**void TestRef(…...
购物网站设计人员/网站建设公司推荐
今天看到有个人 把 docker 部署Gitlab 的文章设置为付费文档了 我就呵呵了。。。 我也来下一篇,免费的 就那么一行命令 sudo docker run --detach \--hostname 115.159.52.223 \--publish 443:443 --publish 80:80 --publish 222:22 \--name gitlab \--restart …...
哪家公司因为做网站失败了/微信裂变营销软件
我们最后的结果还是很仓促,不是很完善,但是我们会在不断的进行中转载于:https://www.cnblogs.com/caicaihong/p/5601988.html...
wordpress 限制游客/企业网站策划
调度算法一、先来先服务FCFS (First Come First Serve)1.思想:选择最先进入后备/就绪队列的作业/进程,入主存/分配CPU2.优缺点优点:对所有作业/进程公平,算法简单稳定缺点:不够灵活,对紧急进程的优先处理权…...
广州大型网站建设/企业管理培训课程
电脑被病毒搞得只好重装系统,重装系统后发生了两件让我生气的小事:MSN Messager无法登陆,系统无法激活。SN无法登陆,错误代码为80048820,google了一下查询!答案是:出现这种问题一般是由于系统时间不对,先校…...
wordpress 当前分类名称/百度推广排名怎么做的
SPKT/DSM接口介绍 给卫星接收无关,是一个叫做JR的遥控器公司给自己遥控器取得名字叫做卫星接收,其实和普通遥控器的差别主要在于信号调制方式不一样。...