进程

进程介绍

进程的概念:进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,是动态的。例如正在运行的QQ,浏览器等。

一个程序运行后至少有一个进程。

那么,多进程有什么用呢?先看到一下程序。

1
2
3
4
5
6
7
def fun_A():
print('任务A')
def fun_B():
print('任务B')

fun_A()
fun_B()

运行这个程序,会按照代码的执行顺序,fun_A函数执行完毕以后才会执行fun_B函数。如果我们让fun_A和fun_B同时运行,那么这个效率显然会大大提升。而这也就是多进程。

进程的创建

  1. 导入进程包

    import multiprocessing

  2. 通过进程类创建进程对象

    进程对象 = multiprocessing.Process(target = 任务名)

    • taget : 这里一般是指函数名,方法名
    • name:进程名,一般不用设置
    • group:进程组,目前只能使用None
  3. 启动进程执行任务

    进程对象.start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time
import multiprocessing # 第一步,导入进程包
# 唱歌函数
def sing():
for i in range(3):
print('唱歌')
time.sleep(0.5) # 设置延迟
# 跳舞函数
def dance():
for i in range(3):
print('跳舞')
time.sleep(0.5)
if __name__ == '__main__':
# 使用进程类创建进程对象
sing_process = multiprocessing.Process(target=sing)
dance_process = multiprocessing.Process(target=dance)
# 启动
sing_process.start()
dance_process.start()

带参数的进程创建

传参有两种方式:

  • args: 以元组的方式传入,顺序一一对应,只有一个参数注意逗号(参数1,)

  • kwargs: 以字典的方式传入,顺序随意,但是key要和函数的参数名字一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import time
    import multiprocessing
    def sing():
    for i in range(3):
    print('唱歌')
    time.sleep(0.5)
    def dance():
    for i in range(3):
    print('跳舞')
    time.sleep(0.5)

    if __name__ == '__main__':
    # 使用进程类创建进程对象
    sing_process = multiprocessing.Process(target=sing)
    dance_process = multiprocessing.Process(target=dance)
    # 启动
    sing_process.start()
    dance_process.start()

获取进程的编号

但程序越来越多时,不好区分进程的之间的关系,为了方便管理,我们需要获取进程编号。

有两种方式:

  • 获取当前进程编号

    os.getpid()

  • 获取当前父进程的编号

    os.getppid()

通俗的说,谁启动就是父进程,这里”main”是主进程,启动了两个子进程,sing_process和dance_process。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time
import os
import multiprocessing # 第一步,导入进程包
# 唱歌函数
def sing(num):
print('我是唱歌进程:',os.getpid())
print('唱歌的父进程:', os.getppid())
for i in range(num):
print('唱歌')
time.sleep(0.5) # 设置延迟
# 跳舞函数
def dance(num):
print('我是跳舞进程:', os.getpid())
print('跳舞的父进程:', os.getppid())
for i in range(num):
print('跳舞')
time.sleep(0.5)
if __name__ == '__main__':
print('我是父进程:', os.getpid())
sing_process = multiprocessing.Process(target=sing,args=(2,))
dance_process = multiprocessing.Process(target=dance,kwargs={'num':1})
sing_process.start()
dance_process.start()

进程注意点:

  1. 主进程会等待所有子进程执行结束再结束
  2. 设置守护子进程 子进程对象.daemon = True ,主进程退出后子进程直接销毁。

案列:文件夹高并发copy器

线程

线程介绍

线程:线程是程序执行的最小单位。同一进程下的线程可以共享进程所拥有的全部资源。(进程是包工头,负责领取资源;线程是工人,负责干活)

线程创建对象

  1. 导入线程模块

    import threading

  2. 通过线程类创建线程对象

    threading.Thread(target=任务名)

  3. 启动线程执行任务

    线程对象.start()

传参和进程是一样的,一并演示。

demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
import os
import threading
def sing(num):
for i in range(num):
print('唱歌')
time.sleep(0.5)

def dance(num):
for i in range(num):
print('跳舞')
time.sleep(0.5)
if __name__ == '__main__':
sing_thread = threading.Thread(target=sing,args=(5,))
dance_thread = threading.Thread(target=dance,kwargs={'num':4})
sing_thread.start()
dance_thread.start()

主线程和子线程的结束顺序

主线程会等待所有的子线程结束在结束

demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import time
import threading

def work():
for i in range(10):
print('工作....')
time.sleep(0.2)

if __name__ == '__main__':
sub_thread = threading.Thread(target=work)
sub_thread.start()

time.sleep(1)
print("主线程结束了") # 主线程其实没有结束,还在等待着子线程

设置守护主线程

  • 创建时设置,如sub_thread = threading.Thread(target=work),daemon=True

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import time
    import threading

    def work():
    for i in range(10):
    print('工作....')
    time.sleep(0.2)

    if __name__ == '__main__':
    sub_thread = threading.Thread(target=work,daemon=True)
    sub_thread.start()

    time.sleep(1)
    print("主线程结束了") # 因为子线程守护主线程,所以主线程和子线程这时候都乖乖结束
  • 使用`线程对象.setDeamon(True)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import time
    import threading

    def work():
    for i in range(10):
    print('工作....')
    time.sleep(0.2)

    if __name__ == '__main__':
    sub_thread = threading.Thread(target=work)
    sub_thread.setDaemon(True)
    sub_thread.start()

    time.sleep(1)
    print("主线程结束了")

线程的执行顺序

线程之间的执行的顺序是无序的

获取当前的线程信息

1
2
3
4
# 通过current_thread方法获取线程对象
current_thread = thread.current_thread()
# 通过current_thread对象可以知道线程的相关信息,例如创建顺序
print(current_thread)

进程和线程的对比

  1. 关系对比

    1. 线程是依附在进程里面的,没有进程就没有线程
    2. 一个进程默认提供一条线程,进程可以创建多个线程
  2. 区别

    1. 创建进程的资源开销要比创建线程的资源开销大
    2. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
    3. 线程不能独立运行
  3. 优缺点对比

    1. 进程优缺点
      • 优点:可以用多核
      • 缺点:资源开销大
    2. 线程优缺点
      • 优点:资源开销小
      • 不能使用多核

案例:高并发cpoy器

多进程高并发cpoy器

  1. 定义源文件夹所在的路径,目标文件夹所在的路径

    1
    2
    source_dir = ""
    dest_dir = ""
  2. 创建目标文件夹

    1
    2
    3
    4
    try:
    os.mkdir(dest_dir)
    except:
    print('目录已存在!')
  3. 通过os.listdir获取源目录的文件列表

    1
    2
    file_list = os.listdir(source_dir)
    # print(file_list)
  4. 遍历每个文件,定义一个函数,专门实现文件拷贝

    1
    2
    for file_name in file_list:
    copy_work(file_name,source_dir,dest_dir)
  5. 对4进行优化,采用进程多任务,完成高并发拷贝

    1
    2
    3
    4
    for file_name in file_list:
    copy_work(file_name,source_dir,dest_dir)
    sub_process = multiprocessing.Process(target=copy_wrok,args=(file_name,source_dir,dest_dir))
    sub_process.start()

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import multiprocessing
import os

def copy_work(file_name,source_dir,dest_dir):
# 拼接文件路径
source_path = source_dir + "/" + file_name
dest_path = dest_dir + "/" + file_name

# 打开源文件和目标文件
with open(source_path,"rb") as source_file:
with open(dest_path,"wb" ) as dest_file:
# 循环读取源文件到目标路径
while True:
data = source_file.read()
if data:
dest_file.write(data)
else:
break



if __name__ == "__main__":
# 定义源文件夹和目标文件夹
source_dir = "D:/test1"
dest_dir = "D:/test2"


# 创建目标文件夹
try:
os.mkdir(dest_dir)
except:
print('目录已存在!')

# 读取源文件夹的文件列表
file_list = os.listdir(source_dir)
print(file_list)

# 遍历文件列表实现拷贝
for file_name in file_list:
copy_work(file_name, source_dir, dest_dir)
sub_process = multiprocessing.Process(target=copy_work, args=(file_name, source_dir, dest_dir))
sub_process.start()