进程
多任务
并发:时间片轮转,第一个进程干0.几毫秒,紧接着轮到第二个,这样转
并行:如果是多核cpu,每个cpu干每个的事情,一起执行
在python中怎么实现多任务
进程和线程还有协程
进程的概念
进程就是问操作系统索要内存空间,真正干活的是线程
进程的作用
小结
- 进程是操作系统进行资源分配的单位
- 进程是python程序中实现多任务的一种方式
多进程的使用(python)
导入进程包
import multiprocessing
Process进程类的说明
玩一玩:
# 1.导入包
import multiprocessing
import time
# 跳舞任务
def dance():
for i in range(3):
print("跳舞中...")
time.sleep(0.2)
# 唱歌任务
def sing():
for i in range(3):
print("唱歌中...")
time.sleep(0.2)
# 2.创建子进程(自己动手创建的进程为子进程,在__init__.py已经导入了Process类)
# 1》group:进程组 目前只能使用None,一般不需要设置
# 2》target: 进程执行的目标任务
# 3》name: 进程名,如果不设置,默认是Process-1,。。。以此类推
dance_process = multiprocessing.Process(target=dance)
# 3.启动进程执行时对应的任务
dance_process.start()
# 主进程执行唱歌任务
sing()
# 1.导入包
import multiprocessing
import time
# 跳舞任务
def dance():
for i in range(3):
print("跳舞中...")
time.sleep(0.2)
# 唱歌任务
def sing():
for i in range(3):
print("唱歌中...")
time.sleep(0.2)
# 2.创建子进程(自己动手创建的进程为子进程,在__init__.py已经导入了Process类)
# 1》group:进程组 目前只能使用None,一般不需要设置
# 2》target: 进程执行的目标任务
# 3》name: 进程名,如果不设置,默认是Process-1,。。。以此类推
dance_process = multiprocessing.Process(target=dance)
sing_process = multiprocessing.Process(target=sing)
# 3.启动进程执行时对应的任务
dance_process.start()
sing_process.start()
# 进程执行是无序的,具体哪个进程先执行是由操作系统调度决定的
dance_process.join()
获取进程编号
能够知道如果获取进程编号
获取进程编号的目的
获取当前进程编号
os.getpid() 表示获取当前进程编号
import multiprocessing
import signal
import time
import os
# 跳舞任务
def dance():
# 获取当前进程(子进程)的编号
dance_process_id = os.getpid()
print("dance_process_id:", dance_process_id, multiprocessing.current_process())
# 获取当前进程父进程的编号
dance_process_parent_id = os.getppid()
print("dance_process的父进程编号是::", dance_process_parent_id)
for i in range(3):
print("跳舞中...")
time.sleep(0.2)
# 扩展:根据进程号,利用kill去杀死进程
os.kill(dance_process_id, 9)
# 唱歌任务
def sing():
sing_process_id = os.getpid()
print("sing_process_id:", sing_process_id, multiprocessing.current_process())
sing_process_parent_id = os.getppid()
print("sing_process的父进程编号是:", sing_process_parent_id)
for i in range(3):
print("唱歌中...")
time.sleep(0.2)
# 获取当前进程(主进程)的编号
main_process_id = os.getpid()
# 获取当前进程对象,查看当前代码是由哪个进程执行的
print("main_process_id:", main_process_id,multiprocessing.current_process())
# 如果不是主文件也需要这个判断
if __name__ == '__main__':
# 创建子进程
dance_process = multiprocessing.Process(target=dance,name='dance_process')
print("dance_process:", dance_process)
sing_process = multiprocessing.Process(target=sing,name='sing_process')
print("sing_process:", sing_process)
# 启动进程执行对应的任务
dance_process.start()
sing_process.start()
# 等待子进程完成
dance_process.join()
sing_process.join()
进程执行带有参数的任务
- 能够写出进程执行带有参数的任务
进程执行带有参数的任务的介绍
args参数的使用
import multiprocessing
def show_info(name, age):
print(name, age)
def show_info_2(name, age):
print(name, age)
if __name__ == '__main__':
# 创建子进程
# 以元组的方式传参
sub_process = multiprocessing.Process(target=show_info, args=("高菲", 20))
# 以字典的方式传参
sub_process_2 = multiprocessing.Process(target=show_info_2, kwargs={"age": 20,"name": "菲阿"})
# 启动进程
sub_process.start()
sub_process_2.start()
# 等待子进程结束
sub_process.join()
sub_process_2.join()
进程的注意点
- 进程之间不共享全局变量
- 主进程会等待所有的子进程执行再结束
进程间不共享全局变量
import multiprocessing
import time
# 定义全局变量
g_list = list()
#添加数据
def add_data():
for i in range(3):
# 列表是可变类型,可以在原有内存的基础上修改数据,并且修改后的内存地址不变
# 所以不需要加上global关键字
# 加上global 表示声明要修改全局变量的内存地址
g_list.append(i)
print("add",i)
time.sleep(0.2)
print("添加完成: ",g_list)
# 读取数据
def read_data():
print("read",g_list)
# 添加数据子进程
add_process = multiprocessing.Process(target=add_data)
# 读取数据子进程
read_process = multiprocessing.Process(target=read_data)
# 启动进程,执行对应的任务
add_process.start()
# 当前进程等待添加数据的进程执行完成一会代码再继续往下执行
add_process.join()
print("main:",g_list)
read_process.start()
# 结论: 进程之间不共享全局变量
主进程会等待所有的子进程执行再结束
import multiprocessing
import time
def task():
for i in range(10):
print("任务执行中...")
time.sleep(0.2)
# 判断是否为主模块,程序入口模块
if __name__ == '__main__':
# 创建子进程
sub_process = multiprocessing.Process(target=task)
# 把子进程设置为守护进程,以后主进程退出子进程之间销毁
sub_process.daemon = True
sub_process.start()
# 主进程延时0.5s
time.sleep(0.5)
# 退出主进程之前先让子进程销毁
sub_process.terminate()
print("over")
# 结论:主进程会等待子进程执行完了再执行
两种方式可以取消:
# 退出主进程之前先让子进程销毁
sub_process.terminate()
# 把子进程设置为守护进程,以后主进程退出子进程之间销毁
sub_process.daemon = True
线程
线程是什么
线程的作用
多线程可以完成多任务
多线程是可以共享全局变量的
线程是python程序中实现多任务的另一种方式,线程的执行需要cpu的调度
多线程的使用
导入线程模块
import threading
线程类Thread参数说明
启动线程
启动线程用strat方法
多线程完成多任务的代码
# 1.导入线程模块
import threading
import time
'''
利用多线程完成 唱歌,跳舞功能
'''
def sing():
# 获取当前线程
current_thread = threading.current_thread()
print("sing:",current_thread)
for i in range(3):
print("唱歌中")
time.sleep(0.2)
def dance():
# 获取当前线程
current_thread = threading.current_thread()
print("dance:", current_thread)
for i in range(3):
print("跳舞中")
time.sleep(0.2)
# 2.创建子线程
if __name__ == '__main__':
# 获取当前线程
current_thread = threading.current_thread()
print("main_thread:", current_thread)
sing_thread = threading.Thread(target=sing, name="sing_thread")
dance_thread = threading.Thread(target=dance, name="dance_thread")
# 3.启动子线程执行对应的任务
sing_thread.start()
dance_thread.start()
sing_thread.join()
dance_thread.join()
线程执行带有参数的任务
线程执行带有参数的任务介绍
args参数的使用
import threading
import time
def show_info(name,age):
print("name: %s age: %d" % (name,age))
if __name__ == '__main__':
# 创建子线程
# 以元组方式传参,一一对应
# sub_thread = threading.Thread(target=show_info,args=("高菲",20))
sub_thread = threading.Thread(target=show_info,kwargs=({"name":"高菲","age":18}))
# 启动线程
sub_thread.start()
线程的注意点
线程之间执行是无序的
import threading
import time
def task():
time.sleep(1)
# 获取当前线程
print(threading.current_thread())
if __name__ == '__main__':
# 循环创建多个线程,模拟测试线程之间执行是否无序
for i in range(20):
# 每循环一次创建一个子线程
sub_thread = threading.Thread(target=task)
# 启动子线程
sub_thread.start()
'具体哪个线程执行是由cpu调度的'
主线程会等待子线程执行结束再结束
import threading
import time
def task():
while True:
print("任务执行中。。。")
time.sleep(0.3)
if __name__ == '__main__':
# 创建子线程
# daemon = Ture 创建守护进程守护主线程
sub_thread = threading.Thread(target=task,daemon=True)
# 还有设置守护进程的方式
# sub_thread.setDaemon(True)
sub_thread.start()
# 让主线程延时执行1S
time.sleep(1)
print("over")
把子线程设置成为守护线程即可
线程之间是可以共享全局变量的
import threading
import time
# 定义全局变量
g_list = []
# 添加任务
def add_data():
for i in range(3):
# 没循环一次把数据添加到全局变量
g_list.append(i)
print("add: ",i)
time.sleep(0.2)
# 代码执行到此,说明添加数据完成
print("添加数据完成:",g_list)
# 读取数据
def read_data():
print("read: ",g_list)
if __name__ == '__main__':
# 创建添加数据的子线程
add_thread = threading.Thread(target=add_data)
# 创建一个读取数据的子线程
read_thread = threading.Thread(target=read_data)
add_thread.start()
add_thread.join()
read_thread.start()
read_thread.join()
# 结论:线程之间共享全局变量
线程之间共享全局变量数据出现错误问题
import threading
# 定义全局变量
g_num = 0
def task1():
for i in range(1000000):
# 没循环一次给全局变量加1
global g_num # 表示要修改全局变量的内存地址
g_num = g_num + 1 # g_num += 1
# 代码执行到此
print("task1:",g_num)
def task2():
for i in range(1000000):
# 没循环一次给全局变量加1
global g_num # 表示要修改全局变量的内存地址
g_num = g_num + 1 # g_num += 1
# 代码执行到此
print("task2:",g_num)
if __name__ == '__main__':
# 创建2个子线程
first_thread = threading.Thread(target=task1)
second_thread = threading.Thread(target=task2)
first_thread.start()
# 线程等待,让第一个线程先执行,然后让第二个线程再执行
first_thread.join() # 主线程等待第一个子线程执行完成以后代码再继续执行
second_thread.start()
互斥锁
互斥锁的使用
使用互斥锁完成2个线程对同一个全局变量各加100万次的操作
import threading
# 定义全局变量
g_num = 0
# 创建互斥锁,Lock本质上是一个函数,通过调用函数可以创建一个互斥锁
lock = threading.Lock()
def task1():
# 上锁
lock.acquire()
for i in range(1000000):
# 没循环一次给全局变量加1
global g_num # 表示要修改全局变量的内存地址
g_num = g_num + 1 # g_num += 1
# 代码执行到此
print("task1:",g_num)
# 释放锁
lock.release()
def task2():
# 上锁
lock.acquire()
for i in range(1000000):
# 没循环一次给全局变量加1
global g_num # 表示要修改全局变量的内存地址
g_num = g_num + 1 # g_num += 1
# 代码执行到此
print("task2:",g_num)
# 释放锁
lock.release()
if __name__ == '__main__':
# 创建2个子线程
first_thread = threading.Thread(target=task1)
second_thread = threading.Thread(target=task2)
first_thread.start()
second_thread.start()
# 互斥锁可以保证同一时刻只有一个线程去执行代码,能够保证全局变量的数据没有问题
# 线程等待和互斥锁都是把多任务改成单任务去执行,保证了数据的准确性,但是执行性能会下降
小结
死锁
死锁:一直等待对方是否锁的情景就是死锁
# 死锁: 一直等待对方是否锁的情景就是死锁
import threading
# 创建互斥锁
lock = threading.Lock()
# 需求:多线程同时根据下标在列表中取值,要保证同一时刻只能有一个线程去取值
def get_value(index):
# 上锁
lock.acquire()
my_list=[1,4,6]
# 判断下标是否越界
if index >= len(my_list):
print("下标越界:",index)
# 取值不成功也需要释放互斥锁,不影响后面的线程去取值
lock.release()
return
# 根据下标取值
value = my_list[index]
print(value)
#释放锁
lock.release()
if __name__ == '__main__':
# 创建大量线程,同时执行根据下标取值的任务
for i in range(10):
# 没循环一次,创建一个子线程
sub_thread = threading.Thread(target=get_value, args=(i,))
# 启动
sub_thread.start()