文章目录
进程
进程的概念
一个运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位。一个进程创建时,操作系统就会给它分配一些资源。一个进程默认有一个线程,线程依附于进程,没有进程就没有线程。
多进程
-
进程类
Process([group [, target [, name [, args [, kwargs]]]]]) group:指定进程组,目前只能使用None target:执行的目标任务名 name:进程名字 args:以元组方式给执行任务传参 kwargs:以字典方式给执行任务传参
-
使用格式
import multiprocessing # 导入进程模块 进程变量名 = multiprocessing.Process(target = 目标任务名) # 创建一个进程 进程变量名.start() # 开始这个进程
-
示例:
import multiprocessing import time def write(): print('我在写作业') def see(): print('我在看电视') def main(): for i in range(5): time.sleep(0.5) process_write = multiprocessing.Process(target=write) process_see = multiprocessing.Process(target= see) process_write.start() process_see.start() if __name__ == '__main__': # 验证是否是在当前文件调用 main()
运行结果:
我在写作业 我在看电视 我在写作业 我在看电视 我在写作业 我在看电视 我在写作业 我在看电视 我在写作业 我在看电视
获取进程号
获取进程号可以用于确定子进程和其父进程之间的关系,能够知道子进程是由哪个父进程创建出来的。
获取当前进程号
-
os.getpid()可以获得当前运行的进程号,需要提前导入os模块
-
格式:
import os os.getpid()
-
示例:
import multiprocessing import time import os def write(): for i in range(5): time.sleep(0.5) print('我在写作业',os.getpid()) def see(): for i in range(5): time.sleep(0.5) print('我在看电视',os.getpid()) def main(): process_write = multiprocessing.Process(target=write) process_see = multiprocessing.Process(target= see) process_write.start() process_see.start() if __name__ == '__main__': main()
运行结果:
我在写作业 10208 我在看电视 7272 我在写作业 10208 我在看电视 7272 我在写作业 10208 我在看电视 7272 我在写作业 10208 我在看电视 7272 我在写作业 10208 我在看电视 7272
获取父进程号
-
os.getppid()可以获得当前运行的进程号,需要提前导入os模块
-
格式:
import os getppid()
-
示例:
import multiprocessing import time import os def write(): for i in range(5): time.sleep(0.5) print('我在写作业,当前进程:%s,父进程:%s'%(os.getpid(),os.getppid())) def see(): for i in range(5): time.sleep(0.5) print('我在看电视,当前进程:%s,父进程:%s'%(os.getpid(),os.getppid())) def main(): print('我是父进程:',os.getpid()) process_write = multiprocessing.Process(target=write) process_see = multiprocessing.Process(target= see) process_write.start() process_see.start() if __name__ == '__main__': main()
运行结果:
我是父进程: 2276 我在写作业,当前进程:3604,父进程:2276 我在看电视,当前进程:9572,父进程:2276 我在看电视,当前进程:9572,父进程:2276 我在写作业,当前进程:3604,父进程:2276 我在看电视,当前进程:9572,父进程:2276 我在写作业,当前进程:3604,父进程:2276 我在写作业,当前进程:3604,父进程:2276 我在看电视,当前进程:9572,父进程:2276 我在看电视,当前进程:9572,父进程:2276 我在写作业,当前进程:3604,父进程:2276
带参数传递的进程
args
-
Process类传递的args参数应和接收函数任务中的形参数量和顺序一致。
-
以元组的形式传递参数
-
示例:
import multiprocessing import time def write(time_): print('我在写作业,想写%s分钟'%time_) def see(time_): print('我在看电视,想看%s分钟'%time_) def main(): for i in range(5): time.sleep(0.5) process_write = multiprocessing.Process(target=write,args = (10,)) process_see = multiprocessing.Process(target= see,args = (60,)) process_write.start() process_see.start() if __name__ == '__main__': # 验证是否是在当前文件调用 main()
运行结果:
我在写作业,想写10分钟 我在看电视,想看60分钟 我在写作业,想写10分钟 我在看电视,想看60分钟 我在写作业,想写10分钟 我在看电视,想看60分钟 我在看电视,想看60分钟 我在写作业,想写10分钟 我在写作业,想写10分钟我在看电视,想看60分钟
kwargs
-
Process类传递的kwargs参数应和接收函数任务中的形参数量和名字一致。
-
以字典的形式传递参数
-
示例:
import multiprocessing import time def write(time_,age): print('今年%s岁,我在写作业,想写%s分钟'%(age,time_)) def see(age,time_): # kwargs传递参数只要名字和数量一致就行,顺序没必要一致 print('今年%s岁,我在看电视,想看%s分钟'%(age,time_)) def main(): for i in range(5): time.sleep(0.5) process_write = multiprocessing.Process(target=write,kwargs = {'time_':10,'age':17}) process_see = multiprocessing.Process(target= see,kwargs = {'time_':10,'age':17}) process_write.start() process_see.start() if __name__ == '__main__': # 验证是否是在当前文件调用 main()
运行结果:
今年17岁,我在写作业,想写10分钟 今年17岁,我在看电视,想看10分钟 今年17岁,我在看电视,想看10分钟今年17岁,我在写作业,想写10分钟 今年17岁,我在看电视,想看10分钟 今年17岁,我在写作业,想写10分钟 今年17岁,我在写作业,想写10分钟 今年17岁,我在看电视,想看10分钟 今年17岁,我在写作业,想写10分钟 今年17岁,我在看电视,想看10分钟
进程之间不共享全局变量
-
创建的子进程会对主进程的资源进行拷贝,拷贝之后的资源是独立的。
-
示例:
import multiprocessing import time age = 17 def write(): global age for i in range(5): time.sleep(0.5) print('今年%d岁,我在写作业'%age) def see(): global age for i in range(5): time.sleep(0.5) age += 1 print('今年%d岁,我在看电视'%age) def main(): process_write = multiprocessing.Process(target=write) process_see = multiprocessing.Process(target= see) process_write.start() process_see.start() time.sleep(5) print('主进程全局变量%d'%age) if __name__ == '__main__': # 验证是否是在当前文件调用 main()
运行结果:
今年18岁,我在看电视 今年17岁,我在写作业 今年17岁,我在写作业 今年19岁,我在看电视 今年17岁,我在写作业今年20岁,我在看电视 今年21岁,我在看电视 今年17岁,我在写作业 今年22岁,我在看电视 今年17岁,我在写作业 主进程全局变量17
主进程会等待所有的子进程结束后再结束
-
若想在主进程结束时同时结束子进程可以使用两种方式:
- 守护进程
- 销毁子进程
-
示例:
import multiprocessing import time def write(): for i in range(5): time.sleep(0.5) print('子进程1还没结束') def see(): for i in range(5): time.sleep(0.5) print('子进程2还没结束') def main(): process_write = multiprocessing.Process(target=write) process_see = multiprocessing.Process(target= see) process_write.start() process_see.start() if __name__ == '__main__': # 验证是否是在当前文件调用 main() time.sleep(1) print('主进程结束')
运行结果:
子进程1还没结束
子进程2还没结束
主进程结束
子进程2还没结束
子进程1还没结束
子进程1还没结束
子进程2还没结束
子进程2还没结束
子进程1还没结束
子进程2还没结束子进程1还没结束
守护进程
-
将Process类中的daemon参数设置为True可以实现结束主进程时终结子进程
-
示例:
import multiprocessing import time def write(): for i in range(5): time.sleep(0.5) print('子进程1还没结束') def see(): for i in range(5): time.sleep(0.5) print('子进程2还没结束') def main(): process_write = multiprocessing.Process(target=write,daemon=True) process_see = multiprocessing.Process(target= see) process_write.start() process_see.daemon = True process_see.start() if __name__ == '__main__': # 验证是否是在当前文件调用 main() time.sleep(1) print('主进程结束')
运行结果:
子进程1还没结束 子进程2还没结束 主进程结束
销毁子进程
-
Process类中的terminate函数可以实现销毁子进程的功能
-
示例:
import multiprocessing import time def write(): for i in range(5): time.sleep(0.5) print('子进程1还没结束') def see(): for i in range(5): time.sleep(0.5) print('子进程2还没结束') if __name__ == '__main__': # 验证是否是在当前文件调用 process_write = multiprocessing.Process(target=write) process_see = multiprocessing.Process(target=see) process_write.start() process_see.start() time.sleep(1) print('主进程结束') process_write.terminate() process_see.terminate()
运行结果:
子进程1还没结束 子进程2还没结束 主进程结束
线程
线程的概念
线程是进程中执行代码的一个分支,由cpu进行调度,线程是cpu调度的基本单位。每个进程都至少有一个线程,也就是主线程。
多个线程可能由一个cpu执行,也可能由多个cpu执行。
python中的线程具有gil(全局解释器锁),cpu会争抢一个进程中的gil,只有抢到gil才可以进行线程代码。这个行为会限制多任务执行的性能,若想提升性能可以用多进程来执行。一个进程具有一个gil。
多线程
使用多线程首先需要导入多线程模块
import threading
线程类Thread参数说明
Thread([group [, target [, name [, args [, kwargs]]]]])
group: 线程组,目前只能使用None
target: 执行的目标任务名
args: 以元组的方式给执行任务传参
kwargs: 以字典方式给执行任务传参
name: 线程名,一般不用设置
格式:
import threading
def a():
print('线程名:',threading.current_thread().name)
# threading.current_thread().name 是当前进程的名字
for i in range(5):
pass
a_thread = threading.Thread(target = a) # 建立一个线程变量,也就是建立一个子线程
a_thread.start() # 开始这个线程
多线程示例:
import threading
import time
def write():
for i in range(10):
print('我在写作业...%s'%i)
time.sleep(0.5)
def see():
for i in range(10):
print('我在看电视...%s'%i)
time.sleep(0.5)
write_thread = threading.Thread(target = write)
see_thread = threading.Thread(target= see)
write_thread.start()
see_thread.start()
运行结果:
我在写作业...0
我在看电视...0
我在写作业...1我在看电视...1
我在看电视...2
我在写作业...2
我在看电视...3我在写作业...3
我在写作业...4
我在看电视...4
我在看电视...5我在写作业...5
我在写作业...6
我在看电视...6
我在写作业...7我在看电视...7
我在写作业...8我在看电视...8
我在写作业...9我在看电视...9
带参数
-
args参数的使用: args传递的是一个元组参数,元组中的项应和函数中的形参一一对应。
import threading import time def write(time_): for i in range(time_): print('我在写作业...%s'%i) time.sleep(0.5) def see(time_): for i in range(time_): print('我在看电视...%s'%i) time.sleep(0.5) time_ = int(input('请输入您想要的次数:')) write_thread = threading.Thread(target = write,args=(time_,)) see_thread = threading.Thread(target= see,args=(time_,)) write_thread.start() see_thread.start()
运行结果:
请输入您想要的次数:6 我在写作业...0 我在看电视...0 我在看电视...1 我在写作业...1 我在写作业...2 我在看电视...2 我在写作业...3 我在看电视...3 我在看电视...4 我在写作业...4 我在写作业...5我在看电视...5
-
kwargs参数的使用:kwargs传递的是一个字典参数,字典中的key应和函数中的形参名字相同并且数量一致。
import threading import time def write(time_): for i in range(time_): print('我在写作业...%s'%i) time.sleep(0.5) def see(time_): for i in range(time_): print('我在看电视...%s'%i) time.sleep(0.5) time_time = {} time_time['time_'] = int(input('请输入您想要的次数:')) write_thread = threading.Thread(target = write,kwargs = {'time_':time_time.get('time_')}) see_thread = threading.Thread(target= see,kwargs = {'time_':time_time.get('time_')}) write_thread.start() see_thread.start()
运行结果:
请输入您想要的次数:4 我在写作业...0 我在看电视...0 我在写作业...1 我在看电视...1 我在写作业...2 我在看电视...2 我在写作业...3 我在看电视...3
多个线程的执行是无序的
import threading
import time
def task():
time.sleep(3)
print("当前线程:", threading.current_thread().name)
if __name__ == '__main__':
for _ in range(5):
sub_thread = threading.Thread(target=task)
sub_thread.start()
运行结果:
当前线程: Thread-3
当前线程: Thread-1
当前线程: Thread-2
当前线程: Thread-4
当前线程: Thread-5
主线程会等待所有子进程都结束后在结束
import threading
import time
def a():
for i in range(5):
print('子进程:%s'%threading.current_thread().name)
time.sleep(1)
a_thread = threading.Thread(target = a)
a_thread.start()
time.sleep(2)
print('主进程结束',threading.current_thread().name)
运行结果:
import threading
import time
def a():
for i in range(5):
print('子进程:%s'%threading.current_thread().name)
time.sleep(1)
a_thread = threading.Thread(target = a)
a_thread.start()
time.sleep(2)
print('主进程结束',threading.current_thread().name)
设置守护线程,使主线程结束时终结子线程
-
设置Thread类中的参数daemon=True实现守护进程
import threading import time def see(): for i in range(10): print('看电视') time.sleep(0.5) thread_see = threading.Thread(target = see,daemon = True) thread_see.start() for i in range(5): print('吃饭') time.sleep(0.5) print('吃完饭了,不能看电视了')
运行结果:
看电视 吃饭 吃饭 看电视 吃饭 看电视 吃饭 看电视 吃饭 看电视 吃完饭了,不能看电视了
-
引用Thread类中的方法setDeamon(True)
import threading import time def see(): for i in range(10): print('看电视') time.sleep(0.5) thread_see = threading.Thread(target = see) thread_see.setDaemon(True) thread_see.start() for i in range(5): print('吃饭') time.sleep(0.5) print('吃完饭了,不能看电视了')
运行结果:
看电视 吃饭 吃饭看电视 吃饭 看电视 吃饭 看电视 吃饭 看电视 吃完饭了,不能看电视了
多个线程共享全局变量
import threading
list1 = []
def writelist():
for i in range(10):
list1.append(i)
print('写完数据啦')
def readlist():
print('开始读数据啦',list1)
w_thread = threading.Thread(target = writelist)
r_thread = threading.Thread(target = readlist)
w_thread.start()
r_thread.start()
运行结果:
写完数据啦
开始读数据啦 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-
当进行大量数据处理时,由于多个线程共享全局变量,所以可能会造成处理结果出错。解决方法:不用多线程,线程等待,互斥锁。
import threading g_num = 0 def sum_num1(): for i in range(1000000): global g_num g_num += 1 print("sum1:", g_num) def sum_num2(): for i in range(1000000): global g_num g_num += 1 print("sum2:", g_num) if __name__ == '__main__': first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) first_thread.start() second_thread.start()
运行结果:
sum1: 1295907 sum2: 1783027 # 计算结果并不是正确的20000000,这是由于g_num+=1在cpu中其实是分三个步骤执行,而进程的切换却可能会打断这三个步骤,使结果出错。
线程等待(join)
-
Thread类中的join函数可以实现线程等待的作用,即一个线程执行结束后才会继续向下执行代码。
import threading g_num = 0 def sum_num1(): for i in range(1000000): global g_num g_num += 1 print("sum1:", g_num) def sum_num2(): for i in range(1000000): global g_num g_num += 1 print("sum2:", g_num) if __name__ == '__main__': first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) first_thread.start() first_thread.join() second_thread.start()
互斥锁
-
Thread类中的Lock()函数可以实现互斥锁的功能。
-
互斥锁可以对共享数据进行锁定,保证同一时刻只有一个线程操作共享数据。
-
互斥锁由多个线程争抢,抢到的执行,未抢到的等待。
-
使用方法:
import threading 互斥锁变量 = threading.lock() # 创建互斥锁 互斥锁变量.acquire() # 上锁 互斥锁变量.release() # 解锁
-
示例:
import threading g_num = 0 g_Lock = threading.Lock() def sum_num1(): for i in range(1000000): global g_num g_Lock.acquire() g_num += 1 g_Lock.release() print("sum1:", g_num) def sum_num2(): for i in range(1000000): global g_num g_Lock.acquire() g_num += 1 g_Lock.release() print("sum2:", g_num) if __name__ == '__main__': first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) first_thread.start() second_thread.start()
运行结果:
sum1: 1680016 sum2: 2000000
-
死锁
-
当互斥锁使用不当时就会出现死锁,类似于死循环,妨碍程序正常运行。
-
示例:
import threading g_num = 0 g_Lock = threading.Lock() def sum_num1(): for i in range(1000000): global g_num g_Lock.acquire() g_num += 1 return g_Lock.release() print("sum1:", g_num) def sum_num2(): for i in range(1000000): global g_num g_Lock.acquire() g_num += 1 g_Lock.release() print("sum2:", g_num) if __name__ == '__main__': first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) first_thread.start() second_thread.start()
运行结果:
# 无结果,且程序一直未结束
-
线程和进程对比
- 进程之间不共享全局变量
- 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
- 创建进程的资源开销要比创建线程的资源开销要大
- 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
- 线程不能够独立执行,必须依存在进程中
- 多进程开发比单进程多线程开发稳定性要强
协程
协程的概念
由于python中的线程存在gil,所以python开发了协程来实现进程中多任务的切换。(进程包含线程包含协程)
协程所消耗的资源小于线程,更小于进程。协程的切换顺序由程序员自己控制,通过代码实现。
协程使用时会创建一个协程管理表,依照协程的创建顺序来执行协程,当一个协程遇到一些耗时操作时(列如输入输出操作),会切换到下一个协程来执行。
进程不共享全局变量,是因为子进程会复制主进程的资源。每个进程至少有一个线程,线程共享全局变量。协程存在于线程中,所以也共享全局变量。
协程的使用方式
-
python类通过协程模块gevent来实现协程
-
当创建一个协程时,该协程自动开始。
-
由于gevent协程是后加的,不在线程的正常使用规则内。所以主线程结束时不会等待协程执行结束,这时就需要通过代码使主线程等待协程结束再结束。
-
gevent无法认知耗时(time.sleep()),所以当需要使用耗时操作时需要引入gevent模块中的monkey类,将耗时代码更改为gevent本身的耗时操作。
-
协程的标准使用格式
import gevent # 导入gevent模块 from gevent import monkey # 导入gevent模块中的monkey类 monkey.patch_all() # 自动将一些gevent不认识的操作替换为gevent本身的操作 协程变量= gevent.spawn(任务名) # 创建一个协程,当创建协程时自动开始此协程 gevent.joinall(协程变量) # 使主线程等待协程结束再结束
-
示例:
import gevent import time from gevent import monkey monkey.patch_all() def write_chinese(): for i in range(10): time.sleep(0.5) print('写语文作业') def write_english(): for i in range(10): time.sleep(0.5) print('写英语作业') ch=gevent.spawn(write_chinese) en=gevent.spawn(write_english) time.sleep(2) print('开始等待协程') gevent.joinall((ch,en)) print('主线程执行结束')
运行结果:
```python
写语文作业
写英语作业
写语文作业
写英语作业
写语文作业
写英语作业
开始等待协程
写语文作业
写英语作业
写语文作业
写英语作业
写语文作业
写英语作业
写语文作业
写英语作业
写语文作业
写英语作业
写语文作业
写英语作业
写语文作业
写英语作业
主线程执行结束