笔记

多进程

多任务

Python可以多进程实习多任务

image-20211229163504272

现在我们的系统就是多任务操作系统

多任务的优势

  • 多任务的最大好处就是充分利用CPU资源,提高程序的执行效率

多任务的概念

多任务就是我们在同一时间可以执行多个任务

多任务执行执行方式

并发

并发就是CPU只有一个核处理多任务,系统程序轮流执行,就是并发

并行

并行就是CPU有多个核处理多任务,一个核处理一个任务,就是并行

进程

什么是进程

进行就是我们资源分配的最小单位,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位

上面说可能不太好理解,

  • 没有运行的程序叫程序

  • 一个运行起来的程序叫做进行

    • 操作系统会给他放屁cpu内存网络等资源

      image-20211229164903224

    • 一个运行起来的程序必须有一个进程

多进程

在程序怎么实现多任务的方法,可以多进程

  • 没有多进行py程序
    • 他运行的时候只有一个住进程
  • 多进程py程序
    • 他运行的时候有多个子进程

py实现多进程

创建多任务

用到的包是multiprocessing

定义进程

1
2
3
4
5
6
import  multiprocessing

# 定义进程对象
进程对象=multiprocessing.Process(target=函数名, name,args,kwargs)
# 启动进程
进程对象.start()

multiprocessing.Process(targt=函数名, name,args,kwargs)

  • target 进行的多进程函数

  • name 可以选参数,设置进程名字,一般不不设置,他会有默认

  • args 元素的方式传参

  • kwargs 字典方式传输

  • group 进程组了解一下就可以了

演示代码

没有添加多进程的

1
2
3
4
5
6
7
8
9
10
11
12
13
def a():
for i in range(3):
print("aaa")

def b():
for i in range(3):
print("bbb")



if __name__=='__main__':
a()
b()

结果

1
2
3
4
5
6
aaa
aaa
aaa
bbb
bbb
bbb

添加了多进程的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import  multiprocessing


def a():
for i in range(3):
print("aaa")

def b():
for i in range(3):
print("bbb")

if __name__=='__main__':
# 定义进程对象a函数
duix1 = multiprocessing.Process(target=a)
# 定义进程对象b函数
duix2 = multiprocessing.Process(target=b)

# 启动进程
duix1.start()
# 启动进程
duix2.start()

结果

1
2
3
4
5
6
aaa
bbb
aaa
bbb
aaa
bbb

创建多任务传参

格式

1
multiprocessing.Process(targt=函数名,args或kwargs)
  • args 元素的方式传参

    • 就是我们传的是按顺序来的
  • kwargs 字典方式传输

    • 就是我们按参数名来的,指定参数名来传递

args元素

格式

1
multiprocessing.Process(targt=函数名,args=(参数,))

args如果就一个参数的话后面要加上**,**应为说明他是一个元素

演示

1
2
3
4
5
6
7
8
9
10
11
def a(a):
for i in range(3):
print(a)


if __name__=='__main__':
# 定义进程对象a函数
duix1 = multiprocessing.Process(target=a,args=('aaa',))

# 启动进程
duix1.start()

结果

1
2
3
aaa
aaa
aaa

kwargs字典

格式

1
multiprocessing.Process(targt=函数名,kwargs={参数的名:传参的内容})

kwargs这个他是按照参数名进行传递的

演示

1
2
3
4
5
6
7
8
9
10
def a(a):
for i in range(3):
print(a)

if __name__=='__main__':
# 定义进程对象a函数
duix1 = multiprocessing.Process(target=a,kwargs={'a':'aaa'})
# 启动进程
duix1.start()

结果

1
2
3
aaa
aaa
aaa

获取进程的编号

用到的是os模块

  1. os.getpid() 获得当前子进程的编号
  2. os.getppid() 获得父进程的编号

代码

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
import  multiprocessing
import os



def a():
print("a父进程" + str(os.getppid()))
print("a子进程"+str(os.getpid()))

def b():
print("b父进程" + str(os.getppid()))
print("b子进程"+str(os.getpid()))



if __name__=='__main__':
print("主进程" + str(os.getppid()))
# 定义进程对象a函数
duix1 = multiprocessing.Process(target=a)
# 定义进程对象b函数
duix2 = multiprocessing.Process(target=b)

# 启动进程
duix1.start()
# 启动进程
duix2.start()

结果

1
2
3
4
5
父进程38072
a父进程52783
a子进程52784
b父进程52783
b子进程52785

守护进程

什么是守护进程主进程关闭其他子进程自动关闭,默认不是

上面说可能不太好理解,比如QQ我们关闭QQ其他的聊天框就会全部关闭这个就是守护进程

image-20211229175332799

python多进程默认不是,他是主进程会等待子进程关闭才会关闭主进程

演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time
import multiprocessing


def a():
for i in range(5):
print("子进程")
time.sleep(0.5)

if __name__=='__main__':
# 定义进程对象a函数
duix1 = multiprocessing.Process(target=a)
# 启动进程
duix1.start()

# 主进程等1秒结束
time.sleep(1)
print("主进程执行完毕")

结果:可以看见主进程执行完毕后主进程在等待子进程执行后在关闭程序

1
2
3
4
5
6
子进程
子进程
主进程执行完毕
子进程
子进程
子进程

我们可以主进程执行后主进程就关闭程序

添加一个:主进程执行后主进程就关闭程序

1
定义进程对象.daemon=True

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import time
import multiprocessing


def a():
for i in range(5):
print("子进程")
time.sleep(0.5)

if __name__=='__main__':
# 定义进程对象a函数
duix1 = multiprocessing.Process(target=a)
duix1.daemon=True
# 启动进程
duix1.start()

# 主进程等1秒结束
time.sleep(1)
print("主进程执行完毕")

结果

1
2
3
子进程
子进程
主进程执行完毕

多线程

多线程也可以实现多任务

有了多进程为什么还要多线程那,应为一旦创建一个进程系统就会给这个进程分配资源

有一个进程才有线程,线程可以有多个或一个(必须有一个),线程是在进程里面的,全部的线程都可以共享这个进程的资源

一个程序能够包含多个进程,一个进程能够启动多个线程,线程才是真正工作的东西

py实现多线程

用到的包是threading,他和上面多进程库使用是一样的

定义进程

1
2
3
4
5
6
import  threading

# 定义线程对象
进程对象=threading.Thread(target=函数名, name,args,kwargs)
# 启动进程
进程对象.start()

multiprocessing.Process(targt=函数名, name,args,kwargs)

  • target 进行的多线程函数

  • name 可以选参数,设置线程名字,一般不不设置,他会有默认

  • args 元素的方式传参

  • kwargs 字典方式传输

  • group 进程组了解一下就可以了

演示代码

没有添加多进程的

1
2
3
4
5
6
7
8
9
10
11
12
13
def a():
for i in range(3):
print("aaa")

def b():
for i in range(3):
print("bbb")



if __name__=='__main__':
a()
b()

结果

1
2
3
4
5
6
aaa
aaa
aaa
bbb
bbb
bbb

添加了多线程的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import  threading
import time

def a():
for i in range(3):
print("aaa")
time.sleep(0.5)

def b():
for i in range(3):
print("bbb")
time.sleep(0.5)

if __name__=='__main__':
# 定义线程对象a函数
duix1 = threading.Thread(target=a)
# 定义线程对象b函数
duix2 = threading.Thread(target=b)

# 启动线程
duix1.start()
# 启动线程
duix2.start()

结果

1
2
3
4
5
6
aaa
bbb
aaa
bbb
aaa
bbb

后面就不演示了和多进程一样

多线程属性

threading我们只知道threading有一个start的开始属性

属性 作用
xxx.start()属性 开始启动线程
xxx.join()属性 阻塞线程直至线程终止,然后在继续运行
xxx.daemon属性 守护进程,和上面一样
xxx.getName()属性 获取线程名称
xxx.setName()属性 用于设置线程的名称

join属性

阻塞线程直至线程终止,然后在继续运行

我自己的解释就是,一个进程里面有一个主线,和守护进程差不多,子线程没有执行完,主线程可能也会主线完

演示

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
import  threading
import time

def a():
for i in range(3):
print("aaa")
time.sleep(0.5)

def b():
for i in range(3):
print("bbb")
time.sleep(0.5)

if __name__=='__main__':
# 定义线程对象a函数
duix1 = threading.Thread(target=a)
# 定义线程对象b函数
duix2 = threading.Thread(target=b)


print("执行第一个线程duix1")
# 启动线程
duix1.start()

# 等duix1线程执行完后在执行后面的程序
duix1.join()

print("执行第二个线程duix2")
# 启动线程
duix2.start() # 这个应为有了join()属性,只有duix1线程执行完后在执行这个duix2

结果,这个应为有了join()属性,只有duix1线程执行完后在执行这个duix2

1
2
3
4
5
6
7
8
执行第一个线程duix1
aaa
aaa
aaa
执行第二个线程duix2
bbb
bbb
bbb

getName()属性

获取线程名称

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

def a():
b=threading.current_thread().getName()
# 获取线程多名字
print(b)

if __name__=='__main__':
# 定义线程对象a函数
duix1 = threading.Thread(target=a)

# 启动线程
duix1.start()

结果

1
Thread-1

setName()属性

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

def a():
b=threading.current_thread().getName()
# 获取线程多名字
print(b)

if __name__=='__main__':
# 定义线程对象a函数
duix1 = threading.Thread(target=a)

# 给线程起名字
duix1.setName("abc")
# 启动线程
duix1.start()

结果

1
abc

线程安全

GIL锁

GIL,中文译为全局解释器锁,我们需要明确的一点是GIL并不是Python的特性,是CPython解释器的特性

GIL锁的作用就是,一个进程中只有一个线程被CPU调用,下一个枪到这个线程枪到这个GIL锁才可以用CPU

线程锁就像嫖妓一样的,你在外面等,别人出来了你才能进去

CPU多核优势,多线程就像嫖妓一样的,一个人玩一个,就可以利用的妓院小姐姐(多核CPU)人多多的优势了