多进程 multiprocessing 模块进程并发Process;Pool ;Queue队列 、threading模块;
multiprocessing 模块中的 Process类提供了跨平台的多进程功能,在windows和linux系统都可以使用。
1、首先要实例化一个类,传入要执行的函数。
实例名 = Process(target=<要执行的函数名>)
2、调用实例的.start()方法启动进程
实例名.start()
Process语法结构如下:
Process([group [, target [, name [, args [, kwargs]]]]])
target:表示这个进程实例所调用对象;
args:表示调用对象的位置参数元组;
kwargs:表示调用对象的关键字参数字典;
name:为当前进程实例的别名;
group:大多数情况下用不到;
Process常用方法:
is_alive() :判断进程实例是否还在运行
join([timeout]) :是否等待子进程实例执行结束,或者等待指定秒数再继续执行
start() :启动进程实例(创建子进程)
run() :如果没有给定target参数,对这个对象调用start()方法时,执行该对象的run()方法
terminate() : 立即终止子进程
Process常用属性:
name :当前进程实例别名,默认为Process-N;N为从1开始递增的证书
pid :当前进程实例的pid
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/6/29 16:45
# @Author:zhangmingda
# @File: multiprocessing_study.py
# @Software: PyCharm
# Description:multiprocessing 模块学习 from multiprocessing import Process
import time
def test():
while True:
print('----Test-----')
time.sleep(1) if __name__ == '__main__':
p = Process(target=test)
p.start() #启动子进程执行test函数 while True:
print('---main主进程----')
time.sleep(1)
输出
---main主进程----
----Test-----
---main主进程----
----Test-----
................
与fork的区别,Process方式的多进程:子进程不结束,主进程不会退出
示例代码:主进程代码瞬间执行到最后,子进程未结束,命令提示符不会出现
# cat multiprocessing_study.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/6/29 16:45
# @Author:zhangmingda
# @File: multiprocessing_study.py
# @Software: PyCharm
# Description:multiprocessing 模块学习 from multiprocessing import Process
import time
def test():
while True:
print('----Test-----')
time.sleep(1) if __name__ == '__main__':
p = Process(target=test)
p.start() #启动子进程执行test函数
# python multiprocessing_study.py
----Test-----
----Test-----
----Test-----
----Test-----
Process.join([timeout]) 阻塞主进程; 等待子进程执行结束,或者阻塞主进程指定秒数
from multiprocessing import Process
import time
def test():
for i in range(0,5):
print('----Test-----%s',i)
time.sleep(1) if __name__ == '__main__':
p = Process(target=test)
p.start() #启动子进程执行test函数
p.join(3)
print('___main_______')
执行输出效果
----Test-----%s 0
----Test-----%s 1
----Test-----%s 2
___main_______
----Test-----%s 3
----Test-----%s 4
通过类的继承:子类方式的方式创建子进程实例
创建新的进程还能够使用类的方式,可以自定义一个类,继承Process类,每次实例化这个类的时候,就等同于实例化一个进程对象,请看下面的实例:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/6/29 19:03
# @Author:zhangmingda
# @File: process_study.py
# @Software: PyCharm
# Description:学习类的继承方式创建新进程
from multiprocessing import Process
import time
import os class SubProgress(Process): def __init__(self,interval):
# 因为Process类本身也有__init__方法,这个子类相当于重写了这个方法,
# 但这样就会带来一个问题,我们并没有完全的初始化一个Process类,所以就不能使用从这个类继承的一些方法和属性,
# 最好的方法就是将继承类本身传递给父类Process.__init__方法,完成这些初始化操作,
# 有两种方式:1. 直接用父类名字.__init__() 2.用super(子类名,self).__init__()
super(SubProgress,self).__init__()
# Process.__init__(self)
self.interval = interval
def run(self):
"""重写Process类中的run()方法"""
print("子进程%s开始执行,父进程为%s" % (os.getpid(),os.getppid()))
start_timestamp = time.time()
time.sleep(self.interval)
stop_timestamp = time.time()
print("子进程:%s执行结束,耗时:%s 秒" % (os.getpid(),stop_timestamp - start_timestamp)) if __name__ == '__main__':
t_start = time.time()
print("当前程序进程%s" % os.getpid())
p1 = SubProgress(1)
#对一个不包含target属性的Process类实例化对象执行start()方法,会运行类中的run()方法
p1.start()
p1.join()
t_stop = time.time()
print('主进程%s执行结束,耗时:%s秒' % (os.getpid(),t_stop - t_start))
进程池Pool
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行,请看下面的实例:
Pool().apply_async(worker任务函数,(参数,...))
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/6/29 19:50
# @Author:zhangmingda
# @File: Pool_study.py
# @Software: PyCharm
# Description:进程池使用
from multiprocessing import Pool
import os
import time
import random def worker(task_num):
for i in range(3):
print("子进程:%s 任务%s: 步骤:%s"% (os.getpid(),task_num,i))
time.sleep(1) if __name__ == '__main__':
# Pool(3)里面的3表示该进程池里面可以同时跑几个进程
pool = Pool(3) for j in range(10):
print('向进程池中添加第%s 个任务' % j)
#如果数量超过了进程池,会暂存在进程池中,for循环不会阻塞
pool.apply_async(worker,(j,)) #向进程池中添加任务的时候同时给任务执行的函数传递参数 pool.close() #禁止再向进程池中添加任务
pool.join() #等待进程池所有任务结束再结束主进程。否则主进程结束子进程随之死亡不会执行
进程池输出效果如下
D:\Python3_study\tornado1\Scripts\python.exe D:/Python3_study/tornado1/test/Pool_study.py
向进程池中添加第0 个任务
向进程池中添加第1 个任务
向进程池中添加第2 个任务
向进程池中添加第3 个任务
向进程池中添加第4 个任务
向进程池中添加第5 个任务
向进程池中添加第6 个任务
向进程池中添加第7 个任务
向进程池中添加第8 个任务
向进程池中添加第9 个任务
子进程:35644 任务0: 步骤:0
子进程:35888 任务1: 步骤:0
子进程:2260 任务2: 步骤:0
子进程:35644 任务0: 步骤:1
子进程:35888 任务1: 步骤:1
子进程:2260 任务2: 步骤:1
子进程:35644 任务0: 步骤:2
子进程:35888 任务1: 步骤:2
子进程:2260 任务2: 步骤:2
子进程:35644 任务3: 步骤:0
子进程:35888 任务4: 步骤:0
子进程:2260 任务5: 步骤:0
子进程:35644 任务3: 步骤:1
子进程:35888 任务4: 步骤:1
子进程:2260 任务5: 步骤:1
子进程:35644 任务3: 步骤:2
子进程:35888 任务4: 步骤:2
子进程:2260 任务5: 步骤:2
子进程:35644 任务6: 步骤:0
子进程:35888 任务7: 步骤:0
子进程:2260 任务8: 步骤:0
子进程:35644 任务6: 步骤:1
子进程:35888 任务7: 步骤:1
子进程:2260 任务8: 步骤:1
子进程:35644 任务6: 步骤:2
子进程:35888 任务7: 步骤:2
子进程:2260 任务8: 步骤:2
子进程:35644 任务9: 步骤:0
子进程:35644 任务9: 步骤:1
子进程:35644 任务9: 步骤:2 Process finished with exit code 0
如上进程池测试代码输出
多进程fork;multiprocessing的Process、Pool的区别
一般fork不用,知道即可。无法跨平台。相对比较底层
Process :一般用在主进程和子进程同时执行任务时使用
Pool进程池: 一般主进程用来等待,每个子进程用来执行具体的任务
进程间通讯Queue队列(from multiprocessing import Queue)
队列的特点:先进先出
q = Queue(3) #表示初始化一个Queue对象,最多可接收三条put消息;当队列满了的时候,再推送会阻塞等待队列被读取有空闲空间后才能继续向队列推送内容。
说明
初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);
Queue.qsize():返回当前队列包含的消息数量;
Queue.empty():判断队列是否为空返回True/False
Queue.full():判断队列是否已满返回True/False
Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除。
block默认值为True,表示无消息可取时,是否阻塞,一直等着新消息来。1. 如果block使用默认值True,消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止;
2. 如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常;
3. 如果设置了timeout (block使用默认值True),则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;
Queue.get_nowait():相当Queue.get(False);
Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True (队列满了是否阻塞);
1. 如果block使用默认值True,消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,
2. 果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常;
3. 如果置了timeout (block=True默认值时),则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
- Queue.put_nowait(item):相当Queue.put(item, False);
示例:
如下示例等于将父进程中的q克隆了一份给子进程,子进程进行了序列化,在子进程中执行后又反序列化给父进程。所以在父进程中能够得到子进程对父进程中数据的修改结果。
进程间通信示例代码 (注意:from multiprocessing import的 Queue队列,只能在from multiprocessing import的Process创建的进程之间共享数据 )
#!/usr/bin/env python
# Author:Zhangmingda
import queue,threading
from multiprocessing import Process,Queue
def f(q):
q.put([234,None,'Hello'])
if __name__ == '__main__':
# q = queue.Queue() #使用线程队列运行会报错TypeError: can't pickle _thread.lock objects
q = Queue() #进程队列
p = Process(target=f,args=(q,)) #启动一个子进程,传递一个队列给子进程,在子进程中向队列内put一个列表[234,None,'Hello']
# p = threading.Thread(target=f,) #启动线程直接共享主进程内存
p.start() #子进程运行
print(q.get()) #主进程中从队列中获取到了子进程推送的数据
p.join() #等待子进程运行结束,主进程再退出
运行效果
C:\Users\Administrator\Desktop\Python3_study\venv\Scripts\python.exe C:/Users/Administrator/Desktop/Python3_study/day10/进程间通信?.py
[234, None, 'Hello']
Process finished with exit code 0
多进程之间共享数据使用from multiprocessing import Manager 类
案例一:使用for循环 利用Process 创建多个子进程(效果类似一个进程之间多线程)
#!/usr/bin/env python
# Author:Zhangmingda from multiprocessing import Process,Manager
import os def f(d,l):
d[os.getpid()] = os.getpid() #对传入的字典进行赋值
l.append(os.getpid()) #向列表中增加数据 if __name__ == '__main__':
with Manager() as manager:
d = manager.dict()
l = manager.list(range(5))
print('初始化一个进程间可以共享的字典和列表:',d,l)
p_list = [] #准备存储进程实例
for i in range(10): #准备启动10个进程
p = Process(target=f,args=(d,l))
p.start()
print('启动第%d个进程'%(i+1))
p_list.append(p)
for res in p_list:
res.join() #等待所有进程运行结束
print(d,l) #主进程获得了子进程修改后的数据
运行效果
C:\Users\Administrator\Desktop\Python3_study\venv\Scripts\python.exe C:/Users/Administrator/Desktop/Python3_study/day10/进程间数据共享manager.py
初始化一个进程间可以共享的字典和列表: {} [0, 1, 2, 3, 4]
启动第1个进程
启动第2个进程
启动第3个进程
启动第4个进程
启动第5个进程
启动第6个进程
启动第7个进程
启动第8个进程
启动第9个进程
启动第10个进程
{6632: 6632, 5300: 5300, 6044: 6044, 2212: 2212, 3828: 3828, 5184: 5184, 5412: 5412, 7000: 7000, 5768: 5768, 5784: 5784} [0, 1, 2, 3, 4, 6632, 5300, 6044, 2212, 3828, 5184, 5412, 7000, 5768, 5784] Process finished with exit code 0
案例二:Pool进程池处理的多个进程之间共享数据
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/6/30 13:46
# @Author:zhangmingda
# @File: multiprocessing_Manager_study.py
# @Software: PyCharm
# Description:多进程之间共享数据(通信)示例代码,多进程使用进程池Pool的方式 from multiprocessing import Manager,Pool
import os def reader(q):
"""读取进程池中的数据"""
print("reader()启动,当前进程:%s, 父进程:%s" % (os.getpid(),os.getppid()))
for i in range(q.qsize()):
print("reader()读取到队列中的消息:%s" % q.get())
def writer(q):
print("writer()启动,当前进程:%s, 父进程:%s" % (os.getpid(),os.getppid()))
for i in "ZhangMingDa":
q.put(i)
if __name__ == '__main__':
print('主进程启动,PID:%s' % os.getpid())
#初始化共享消息的队列
q = Manager().Queue()
# 准备进程池实例
pool = Pool()
# 使用阻塞模式创建进程,这样就无需在reader中写死循环了,可以writer()后再reader()
pool.apply(writer,(q,))
pool.apply(reader,(q,))
pool.close() # 封闭进程池,禁止再添加任务
pool.join() # 等待子进程执行结束再退出主进程
print("主进程结束:PID:%s" % os.getpid())
执行效果输出
D:\Python3_study\tornado1\Scripts\python.exe D:/Python3_study/tornado1/test/multiprocessing_Manager_study.py
主进程启动,PID:13888
writer()启动,当前进程:16536, 父进程:13888
reader()启动,当前进程:14028, 父进程:13888
reader()读取到队列中的消息:Z
reader()读取到队列中的消息:h
reader()读取到队列中的消息:a
reader()读取到队列中的消息:n
reader()读取到队列中的消息:g
reader()读取到队列中的消息:M
reader()读取到队列中的消息:i
reader()读取到队列中的消息:n
reader()读取到队列中的消息:g
reader()读取到队列中的消息:D
reader()读取到队列中的消息:a
主进程结束:PID:13888
Process finished with exit code 0
案例三:多进程复制文件
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/6/30 22:28
# @Author:zhangmingda
# @File: Pool_Queue_copy_file.py
# @Software: PyCharm
# Description:多进程拷贝文件代码示例 from multiprocessing import Pool, Manager
#使用Pool进程池执行多个进程,Manager().Queue() 进程间通信 import os
import sys def copy_file(old_dir_name,new_dir_name,filename,queue):
"""
:param old_dir_name:
:param new_dir_name:
:param filename:
:param queue:
:return:
"""
queue.put(filename) #这里有个待解的疑问:放在函数第一行,所有文件名都能添加到队列里面 rf = open(os.path.join(old_dir_name, filename), 'r')
wf = open(os.path.join(new_dir_name,filename), 'w')
content = rf.read() # 读取所有数据
wf.write(content)
rf.close()
wf.close()
#queue.put(filename) 这句话如果放到这里跑,队列里面就只能put进去2个文件名 ,然后就没有然后了,导致整个程序卡在main()主进程的while循环里面 def main():
old_dir_name = sys.argv[1]
new_dir_name = old_dir_name + "副本"
#创建新文件夹
os.mkdir(new_dir_name)
filenames = os.listdir(old_dir_name)
pool = Pool(4)
queue = Manager().Queue() #存储已完成的文件的文件名
for filename in filenames:
pool.apply_async(copy_file,(old_dir_name,new_dir_name,filename,queue)) old_file_num = len(filenames)
while queue.qsize() <= old_file_num:
print("\r 已拷贝文件%s/%s" % (queue.qsize(),old_file_num),end='')
if queue.qsize() == old_file_num:
print("完成")
break if __name__ == '__main__':
main()
(实际进程间还是copy了同样的数据最后汇总,Manager()自己内部有锁)
Pool进程池任务返回值获取和使用
进程池 Pool+回调函数示例代码
#!/usr/bin/env python
# Author:Zhangmingda
from multiprocessing import Pool
import time,os def Foo(i):
time.sleep(1)
print('in process',os.getpid())
return i + 100 #return 的值可以被当做参数传给回调函数获取 def Bar(arg): # 函数作为回调函数使用时;形参自动被赋值为进程池执行任务的返回值
print('-->exec done:',arg,os.getpid()) if __name__ == '__main__':
pool = Pool(processes=3) #限制可以同时运行的进程数量
print('主进程ID:',os.getpid())
for i in range(10):
# pool.apply(func=Foo,args=(i,)) #串行
# pool.apply_async(func=Foo,args=(i,)) #并行
pool.apply_async(func=Foo,args=(i,),callback=Bar) #并行+回调主进程运行
print('for 循环 END。。。。。')
pool.close()
pool.join() #等待所有进程运行结束,否则会直接结束运行。
运行效果
C:\Users\Administrator\Desktop\Python3_study\venv\Scripts\python.exe C:/Users/Administrator/Desktop/Python3_study/day10/进程池(限制同时运行的进程数量).py
主进程ID: 5508
for 循环 END。。。。。
in process 6856
-->exec done: 100 5508
in process 6620
-->exec done: 101 5508
in process 7988
-->exec done: 102 5508
in process 6856
-->exec done: 103 5508
in process 6620
-->exec done: 104 5508
in process 7988
-->exec done: 105 5508
in process 6856
-->exec done: 106 5508
in process 6620
-->exec done: 107 5508
in process 7988
-->exec done: 108 5508
in process 6856
-->exec done: 109 5508 Process finished with exit code 0
进程无法使用queue线程队列,threading线程才可以使用
示例代码:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/7/1 21:54
# @Author:zhangmingda
# @File: threading_Thread_Queue.py
# @Software: PyCharm
# Description: #!/usr/bin/env python
# Author:Zhangmingda
import queue #线程队列
import threading #多线程模块 from multiprocessing import Process,Queue #多进程 进程队列 def f(q):
q.put([234,None,'Hello'])
if __name__ == '__main__':
q = queue.Queue() #使用线程队列
# p = Process(target=f,args=(q,)) #启动一个子进程 运行报错TypeError: can't pickle _thread.lock objects
p = threading.Thread(target=f,args=(q,)) #启动线程直接共享主进程内存
p.start()
print(q.get())
p.join()
C:\Users\Administrator\Desktop\Python3_study\venv\Scripts\python.exe C:/Users/Administrator/Desktop/Python3_study/day10/进程间通信?.py
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/Python3_study/day10/进程间通信?.py", line 13, in <module>
p.start()
File "C:\Program Files\Python36\lib\multiprocessing\process.py", line 105, in start
self._popen = self._Popen(self)
File "C:\Program Files\Python36\lib\multiprocessing\context.py", line 223, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "C:\Program Files\Python36\lib\multiprocessing\context.py", line 322, in _Popen
return Popen(process_obj)
File "C:\Program Files\Python36\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
reduction.dump(process_obj, to_child)
File "C:\Program Files\Python36\lib\multiprocessing\reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
TypeError: can't pickle _thread.lock objects
多进程 multiprocessing 模块进程并发Process;Pool ;Queue队列 、threading模块;的更多相关文章
- Python 多进程编程之 进程间的通信(Queue)
Python 多进程编程之 进程间的通信(Queue) 1,进程间通信Process有时是需要通信的,操作系统提供了很多机制来实现进程之间的通信,而Queue就是其中的一个方法----这是操作系统开辟 ...
- 多任务fork、multiprocessing、进程池、进程间通信-Queue
并发:一个处理器同时处理多个任务. 并行:多个处理器或者是多核的处理器同时处理多个不同的任务. fork创建子进程 import os import time #fork出一个子进程,子进程也从这一行 ...
- 13、多进程multiprocessing、进程池
内容相关: multiprocessing: 进程的创建与运行 进程常用相关函数 进程池: 为什么要有进程池 进程池的创建与运行:串行.并行 回调函数 多进程multiprocessing: pyth ...
- python并发编程-进程间通信-Queue队列使用-生产者消费者模型-线程理论-创建及对象属性方法-线程互斥锁-守护线程-02
目录 进程补充 进程通信前言 Queue队列的基本使用 通过Queue队列实现进程间通信(IPC机制) 生产者消费者模型 以做包子买包子为例实现当包子卖完了停止消费行为 线程 什么是线程 为什么要有线 ...
- Python的并发并行[1] -> 线程[0] -> threading 模块
threading模块 / threading Module 1 常量 / Constants Pass 2 函数 / Function 2.1 setprofile()函数 函数调用: thread ...
- 并发编程——多进程——multiprocessing开启进程的方式及其属性(3)
开启进程的两种方式——Process 方式一:函数方法 from multiprocessing import Process import time def task(name): print('% ...
- multiprocessing的进程通信Pipe和Queue
pipe管道,2个进程通信,允许单项或者双向,multiprocessing.Pipe(duplex=False)为单项,默认双向 示例: from multiprocessing import Pr ...
- Python第十五天 datetime模块 time模块 thread模块 threading模块 Queue队列模块 multiprocessing模块 paramiko模块 fabric模块
Python第十五天 datetime模块 time模块 thread模块 threading模块 Queue队列模块 multiprocessing模块 paramiko模块 fab ...
- 进程,多进程,进程与程序的区别,程序运行的三种状态,multiprocessing模块中的Process功能,和join函数,和其他属性,僵尸与孤儿进程
1.进程 什么是进程: 一个正在被运行的程序就称之为进程,是程序具体执行的过程,是一种抽象概念,进程来自操作系统 2.多进程 多个正在运行的程序 在python中实现多线程的方法 from mult ...
随机推荐
- 用Python画一个八角形代码示例
import turtle turtle.color("purple","yellow") turtle.speed(1) turtle.fd(100) t ...
- CF1373G
考虑中间格子不能有相同的点,其实是没用的. 其唯一用处是用来规定最后的是无法重叠的. 我们可以证明最后位置的无重叠和中间不重叠是充要的. 那显然可以我们对每个点往后连边: 形式的话的说: 对 \((x ...
- 洛谷 P7451 - [THUSCH2017] 杜老师(线性基+根分+结论题)
题面传送门 看到乘积为平方数我们可以很自然地想到这道题,具体来说,我们对 \(1\sim 10^7\) 中所有质因子标号 \(1,2,\cdots,\pi(10^7)\),对于 \(x\in[l,r] ...
- 利用elliipse做相关图
参考资料:<数据探掘 R语言实战> p65-P68 install.packages("rattle") # 获取实验数据集 install.packages(&quo ...
- 去空格及换行制表符【c#】
string returnStr = tbxContractNO.Text.Replace("\n", "").Replace(" ", & ...
- vim文本编辑器的基本使用
vim文本编辑器的基本使用 1. vi和vim的区别和联系 可以说vim是vi的增强版,在使用vim编辑文本时,可以根据字体颜色来判断编写程序的正确性. 2. vim文本编辑器的常用命令 1. 编辑指 ...
- CR LF 的含义
可以参考: 转载于:https://www.cnblogs.com/babykick/archive/2011/03/25/1995977.html
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(终)-配合内存管理来遍历SD卡
[STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...
- Linux系统中安装软件方法总结
Linux系统中安装软件方法总结 [1]Linux系统中安装软件的几种方式 [2] Linux配置yum源(本地源和网络源) [3] SuSE下zypper源配置 [4] SUSE zypper 本地 ...
- IDEA2021.2安装与配置
https://blog.csdn.net/qq_37242720/article/details/119349394