目录
并发并行:
使用场景
进程:
多进程完成多任务的代码
获取进程编号
进程执行带有参数的任务
进程之间不共享全局变量
主进程会等待所有的子进程执行结束再结束
线程
线程的注意点介绍
多线程同时对全局变量进行 *** 作出现计算问题
互斥锁:
死锁;
进程线程对比:
2. 区别对比
3. 优缺点对比
4. 小结
并发并行:
python是解释器逐行解释的,默认是不能实现代码中的方法或函数同时运行的,
此时开启多进程或者多线程即可实现!
并发并行
并发: 单核多线程交替执行
在一段时间内交替去执行任务。
例如:
对于单核cpu处理多任务, *** 作系统轮流让各个软件交替执行,假如:软件1执行0.01秒,切换到软件2,软件2执行0.01秒,再切换到软件3,执行0.01秒……这样反复执行下去。表面上看,每个软件都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像这些软件都在同时执行一样,这里需要注意单核cpu是并发的执行多任务的。
并行: 多核实现多软件一起执行
对于多核cpu处理多任务, *** 作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件。这里需要注意多核cpu是并行的执行多任务,始终有多个软件一起执行。
主进程子进程 执行此代码的进程 子进程 主进程创建的进程,主子线程也是一样!
使用场景多进程适合在cpu 密集型 *** 作(cpu *** 作指令比较多,如位数多的浮点运算)
多线程适合在 IO密集型 *** 作(读写数据 *** 作比较多的,比如爬虫)
进程:Process([group [, target [, name [, args [, kwargs]]]]])
group:指定进程组,目前只能使用Nonetarget:执行的目标任务名name:进程名字args:以元组方式给执行任务传参kwargs:以字典方式给执行任务传参
Process创建的实例对象的常用方法:
start():启动子进程实例(创建子进程)join():等待子进程执行结束is_alive() 是否存活terminate():不管任务是否完成,立即终止子进程
Process创建的实例对象的常用属性:
子进程.name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
multiprocessing.current_process().name 主进程名字多进程完成多任务的代码
import multiprocessing import time # 跳舞任务 def dance(): for i in range(5): print("跳舞中...") time.sleep(0.2) # 唱歌任务 def sing(): for i in range(5): print("唱歌中...") time.sleep(0.2) if __name__ == '__main__': # 创建跳舞的子进程 # group: 表示进程组,目前只能使用None # target: 表示执行的目标任务名(函数名、方法名) # name: 进程名称, 默认是Process-1, ..... dance_process = multiprocessing.Process(target=dance, name="myprocess1") sing_process = multiprocessing.Process(target=sing) # 启动子进程执行对应的任务 dance_process.start() sing_process.start() ''' 唱歌中... 跳舞中... 唱歌中...跳舞中... 跳舞中...唱歌中... 跳舞中...唱歌中... 唱歌中...跳舞中... '''获取进程编号
获取当前进程编号
os.getpid()获取当前父进程编号
os.getppid()获取进程编号可以查看父子进程的关系
import multiprocessing import os import time def sing(): # 获取当前进程的编号 print('sing当前进程的编号',os.getpid()) # 获取当前进程 print('sing当前进程',multiprocessing.current_process().name) for i in range(5): print('唱歌中...') time.sleep(.5) def dance(): # 获取当前进程的编号 print('dance当前进程的编号', os.getpid()) # 获取当前进程 print('dance当前进程', multiprocessing.current_process().name) for i in range(5): print('跳舞中...') time.sleep(.5) if __name__=='__main__': #获取当前进程编号 print('主进程编号',os.getpid()) # 获取当前进程 print('当前进程',multiprocessing.current_process().name) sing_pro=multiprocessing.Process(target=sing) dance_pro=multiprocessing.Process(target=dance) dance_pro.start() sing_pro.start() ''' 主进程编号 33156 当前进程 MainProcess sing当前进程的编号 35484 sing当前进程 Process-1 唱歌中... dance当前进程的编号 33880 dance当前进程 Process-2 跳舞中... 唱歌中... 跳舞中... 跳舞中...唱歌中... 唱歌中...跳舞中... 唱歌中...跳舞中... '''进程执行带有参数的任务
args 元组
import multiprocessing import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..") time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子进程 # args: 以元组的方式给任务传入参数 sub_process = multiprocessing.Process(target=task, args=(5,)) sub_process.start()
执行结果:
任务执行中.. 任务执行中.. 任务执行中.. 任务执行中.. 任务执行中.. 任务执行完成
kwargs 字典
import multiprocessing import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..") time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子进程 # kwargs: 表示以字典方式传入参数 sub_process = multiprocessing.Process(target=task, kwargs={"count": 3}) sub_process.start()
执行结果:
任务执行中.. 任务执行中.. 任务执行中.. 任务执行完成
注意:字典中的key一定要和参数名保持一致
进程之间不共享全局变量*** 作进程间的全局列表,a子进程增加列表数据,主进程和其他子进程无法获取a添加的数据
import multiprocessing glist=list() # args参数是元组 def add_data(): for i in range(5): glist.append(i) else: print('增加任务执行完毕',glist) def read_data(): print('read',glist) if __name__=='__main__': add_pro=multiprocessing.Process(target=add_data) read_pro=multiprocessing.Process(target=read_data) add_pro.start() add_pro.join() read_pro.start() print('main',glist) ''' 增加任务执行完毕 [0, 1, 2, 3, 4] main [] read [] '''主进程会等待所有的子进程执行结束再结束
意思就是主进程结束了 子进程若没执行完毕依然会执行
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.start() # 主进程延时0.5秒钟 time.sleep(0.5) print("over") exit() # 总结: 主进程会等待所有的子进程执行完成以后程序再退出
执行结果:
任务执行中... 任务执行中... 任务执行中... over 任务执行中... 任务执行中... 任务执行中... 任务执行中... 任务执行中... 任务执行中... 任务执行中...
进程是同时执行的,此时子进程0.2秒执行一次 主进程0.5秒后退出,此时就出现以上顺序结果!
主进程执行完,子进程也跟着执行完的办法:
1.让子进程去守护主进程,子进程跟随主进程的生命周期子进程对象.daemon = True
2.或者再主进程执行完毕前让子进程终止执行子进程对象.terminate()
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() time.sleep(0.5) print("over") # 让子进程销毁 sub_process.terminate() exit() # 总结: 主进程会等待所有的子进程执行完成以后程序再退出 # 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁
执行结果:
任务执行中... 任务执行中... 任务执行中... over线程
线程是实现多任务的另外一种方式
线程使用方式和进程一致
import threading import time def sing(count): for i in range(count): print("正在唱歌...%d" % i) time.sleep(1) def dance(count): for i in range(count): print("正在跳舞...%d" % i) time.sleep(1) if __name__=='__main__': sing=threading.Thread(target=sing,args=(5,)) dance=threading.Thread(target=dance,kwargs={'count':5}) sing.start() dance.start()
结果是交替执行的
线程的注意点介绍- 线程之间执行是无序的主线程会等待所有的子线程执行结束再结束线程之间共享全局变量线程之间共享全局变量数据出现错误问题
import threading import time def task(): time.sleep(1) print("当前线程:", threading.current_thread().name) if __name__ == '__main__': for _ in range(5): sub_thread = threading.Thread(target=task) sub_thread.start()
执行结果:
当前线程: Thread-1 当前线程: Thread-2 当前线程: Thread-4 当前线程: Thread-5 当前线程: Thread-3
说明:
线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就先执行,没有调度的线程不能执行。进程之间执行也是无序的,它是由 *** 作系统调度决定的, *** 作系统调度哪个进程,哪个进程就先执行,没有调度的进程不能执行。
守护主线程
- threading.Thread(target=show_info, daemon=True)线程对象.setDaemon(True)
import threading import time # 测试主线程是否会等待子线程执行完成以后程序再退出 def show_info(): for i in range(5): print("test:", i) time.sleep(0.5) if __name__ == '__main__': # 创建子线程守护主线程 # daemon=True 守护主线程 # 守护主线程方式1 sub_thread = threading.Thread(target=show_info, daemon=True) # 设置成为守护主线程,主线程退出后子线程直接销毁不再执行子线程的代码 # 守护主线程方式2 # sub_thread.setDaemon(True) sub_thread.start() # 主线程延时1秒 time.sleep(1) print("over")
执行结果:
test: 0 test: 1 over
线程间的全局数据共享
import threading import time # 定义全局变量 my_list = list() # 写入数据任务 def write_data(): for i in range(5): my_list.append(i) time.sleep(0.1) print("write_data:", my_list) # 读取数据任务 def read_data(): print("read_data:", my_list) if __name__ == '__main__': # 创建写入数据的线程 write_thread = threading.Thread(target=write_data) # 创建读取数据的线程 read_thread = threading.Thread(target=read_data) write_thread.start() # 延时 # time.sleep(1) # 主线程等待写入线程执行完成以后代码在继续往下执行 write_thread.join() print("开始读取数据啦") read_thread.start()
执行结果:
write_data: [0, 1, 2, 3, 4] 开始读取数据啦 read_data: [0, 1, 2, 3, 4]多线程同时对全局变量进行 *** 作出现计算问题
import threading num=0 def sum_num1(): for i in range(1000000): global num num+=1 print('sum1',num) def sum_num2(): for i in range(1000000): global num num+=1 print('sum2',num) if __name__ == '__main__': one=threading.Thread(target=sum_num1) two=threading.Thread(target=sum_num2) one.start() two.start() ''' sum1 1503155 sum2 2000000 '''
错误分析:
两个线程first_thread和second_thread都要对全局变量g_num(默认是0)进行加1运算,但是由于是多线程同时 *** 作,有可能出现下面情况:
- 在g_num=0时,first_thread取得g_num=0。此时系统把first_thread调度为”sleeping”状态,把second_thread转换为”running”状态,t2也获得g_num=0然后second_thread对得到的值进行加1并赋给g_num,使得g_num=1然后系统又把second_thread调度为”sleeping”,把first_thread转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。这样导致虽然first_thread和first_thread都对g_num加1,但结果仍然是g_num=1
总结 其实线程交替完成执行的,一个再执行其余的就是休眠状态,
解决方案就是线程异步执行变为同步,
- 线程等待(join)互斥锁
import threading # 定义全局变量 g_num = 0 # 循环1000000次每次给全局变量加1 def sum_num1(): for i in range(1000000): global g_num g_num += 1 print("sum1:", g_num) # 循环1000000次每次给全局变量加1 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()
执行结果:
sum1: 1000000 sum2: 2000000互斥锁:
全局互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去 *** 作。
就是有一把线程锁 a b 2个子线程执行的时候 执行到a 则给a先上锁 执行完毕再释放锁,此时轮到b执行方式也是一样
lock.acquire() 上锁 lock.release() 释放锁
import threading num=0 # 全局锁 lock=threading.Lock() def sum_num1(): lock.acquire() for i in range(1000000): global num num+=1 print('sum1',num) lock.release() def sum_num2(): lock.acquire() for i in range(1000000): global num num+=1 print('sum2',num) lock.release() if __name__ == '__main__': one=threading.Thread(target=sum_num1) two=threading.Thread(target=sum_num2) one.start() # one.join() two.start() ''' sum1 1000000 sum2 2000000 '''死锁;
就是互斥锁使用的时候若出现return break if else 等关键字执行的时候难免出现执行不到方法的最后此时,只是再函数的最后释放锁,此时return出去了,此释放锁没有执行到,造成死锁!
一处上锁 多出释放锁!
import threading import time # 创建互斥锁 lock = threading.Lock() # 根据下标去取值, 保证同一时刻只能有一个线程去取值 def get_value(index): # 上锁 lock.acquire() print(threading.current_thread()) my_list = [3,6,8,1] # 判断下标释放越界 if index >= len(my_list): print("下标越界:", index) # 这个位置也要释放 lock.release() return value = my_list[index] print(value) time.sleep(0.2) # 释放锁 lock.release() if __name__ == '__main__': # 模拟大量线程去执行取值 *** 作 for i in range(30): sub_thread = threading.Thread(target=get_value, args=(i,)) sub_thread.start()进程线程对比: 2. 区别对比
进程之间不共享全局变量
线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
创建进程的资源开销要比创建线程的资源开销要大
进程是 *** 作系统资源分配的基本单位,线程是CPU调度的基本单位
线程不能够独立执行,必须依存在进程中
多进程开发比单进程多线程开发稳定性要强
进程优缺点:
优点:可以用多核缺点:资源开销大线程优缺点:
优点:资源开销小缺点:不能使用多核 4. 小结
进程和线程都是完成多任务的一种方式多进程要比多线程消耗的资源多,但是多进程开发比单进程多线程开发稳定性要强,某个进程挂掉不会影响其它进程。多进程可以使用cpu的多核运行,多线程可以共享全局变量。线程不能单独执行必须依附在进程里面
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)