python进阶之 进程&线程区别
1.进程创建方式
import time import os from multiprocessing import Process def func (): time.sleep(1) print(1,os.getpid(),os.getppid()) if __name__ == '__main__': #异步执行func,在一秒钟的时间通过生成多个子进程来执行多个func #1.先实例化 #2.实例化对象.start() # 目标函数:target= Process(target=func).start() #process类通知操作系统要为当前代码快在开辟一个进程,是异步过程
import time from multiprocessing import Process class Myprocess(Process): #继承Process类 def __init__(self,name): #重新父类的__init__方法 super().__init__() self.name =name def run(self): #必须是run函数名 print(os.getpid(),os.getppid(),'我爱你%s' % self.name) time.sleep(3) if __name__ == '__main__': start_time = time.time() p1 = Myprocess('kobe') p2 = Myprocess('kobe') p3 = Myprocess('kobe') p1.start() p2.start() p3.start() p1.join() #阻塞,直到p1对应的进程结束之后,才结束阻塞,是异步阻塞,阻塞等待一个子进程结束 p2.join() p3.join() print(time.time()-start_time) ##阻塞一个子进程:p.join() #阻塞多个子进程,将子进程加入到列表,然后分别阻塞 ''' 10600 9384 我爱你kobe 10448 9384 我爱你kobe 10032 9384 我爱你kobe 1.1659998893737793 从上面结果来看,我们知道join也是异步阻塞的,在同一秒钟分别执行了p1,p2,p3的time.sleep() 和我本来预想的要阻塞3s+是结果是不同的 '''
2.线程创建方式
from threading import Thread import os def func(): print('in fucn ',os.getpid()) print('in main ',os.getpid()) Thread(target=func).start() #两个打印pid是一样的,说明线程是由进程产生的
from threading import Thread import time class Sayhi(Thread): def __init__(self,name): super().__init__() #重写父类的__init__方法 self.name=name def run(self): time.sleep(2) print('%s say hello' % self.name) if __name__ == '__main__': t = Sayhi('kobe') t.start() print('主线程')
3.进程和线程的区别
线程和进程的区别
进程: 数据隔离(但是也可以丛进程间的数据共享) 开销大
线程: 数据共享 开销小
cpython中多进程可以使用多核(并发编程),多线程不可以使用多核
通俗来讲:比如有个4核(cpu)的主机,当使用多进程的时候,系统会自动的根据进程调度算法来分配今晨给资源,然后分别在不同的cpu上面执行,这就是多进程
当时用多线程的时候,由于GIL锁的存在,多线程始终是在一个cpu上面执行,造成闲的cpu闲死,忙的cpu忙死
进程和线程的区别:
1.进程数据隔离,线程中的一部分数据共享
2.进程创建\销毁\上下文切换资源消耗较大,线程较小
3.父进程和子进程的pid不同,所以都有各自的内存空间,
4.创建方式不同,进程床架你是通过fork方式来创建的,完全复制父进程的环境变量 变量 程序计数器等等,到那时是作为父进程的子进程
5.python中的多进程可以使用多核,但是多线程由于gil锁的存在,所以不能使用多核,gil锁导致同一时间同一个进程中的多个线程,有且只有一个可以获得cpu来做任务
6.线程必须要依赖进程或者应用程序来运行,每一个独立的线程有程序运行入口,顺序执行序列和应用程序出口
7.多线程的意义在于:在应用程序中有多个可执行部分可以同时执行,但是操作系统不会将多个线程当做多个独立的应用来实现进程搞得调度管理和资源分配。这是线程和进程的最大区别
os.getpid()不同
#线程 from threading import Thread import os import time def func(): print('in func',os.getpid()) tt = Thread(target=func) tt.start() print('in main', os.getpid())
#进程 from multiprocessing import Process import os import time def func(): print('in func',os.getpid()) if __name__ == '__main__': tt = Process(target=func) tt.start() print('in main', os.getpid())
注意:线程不需要if __name__ == '__main__':
进程和线程的创建原理不同,所以不需要if __name__ == '__main__',但是可以使用这种方式
因为新的线程是在主线程的内存中,所以新的线程和主线程共享同一段代码,不需要import导入,也就不存在子线程中又重复一次创建线程的过程
二者pid对比:
在进程内,父进程创建的所有子进程都是不同的pid,说明父进程只是创建了子进程并在子进程执行代码的时候会import 父进程文件,其余父子相互之间独立,使用不同的内存空间,所以pid不同
在线程中,主线程和子线程的pid完全相同,说明子线程是在主线程内部创建出来的,并且主线程会所有子线程的结束才结束。
二者开销对比
#线程开销时间 import time from threading import Thread def func(item): item +=1 if __name__ == '__main__': start = time.time() t_l = [] for i in range(100): t = Thread(target=func,args=(i,)) t.start() t_l.append(t) for t in t_l: t.join() print(time.time()-start) #进程开销时间 import time def func(item): item +=1 from multiprocessing import Process if __name__ == '__main__': start = time.time() t_l = [] for i in range(100): t = Process(target=func,args=(i,)) t.start() t_l.append(t) for t in t_l: t.join() print(time.time()-start)
注意:为什么要join()?
因为要等待所有线程或进程全部都运行完,才计算时间
不能再func()函数里面写join()方法,这样就是同一时间只有一个线程或进程在之心,变成同步的了
二者开销对比:
分别执行上面代码,能够看到,做相同的操作,100个进程使用的时间比100个线程使用时间更长
说明进程在创建\销毁\上下文切换之间,需要更多的时间和系统资源
二者数据是否共享
#多个线程之间共享全局变量 from threading import Thread item = 0 def func(): global item item +=1 f_l= [] for ii in range(1000): tt = Thread(target=func) tt.start() f_l.append(tt) [x.join() for x in f_l ] print(item) #多个进程之间数据不共享(但是可以通过Manager来实现共享) from multiprocessing import Process,Manager item = 0 def func(): global item item +=1 if __name__ == '__main__': f_l= [] for ii in range(10): tt = Process(target=func) tt.start() f_l.append(tt) [x.join() for x in f_l ] print(item)
二者数据是否共享:
在线程内,主线程内的全局变量对于子线程来说完全共享,其他不是全局的就不共享
在进程内,父进程和子线程的数据完全隔离,因为父子之间分别有不同的内存空间。
关于进程和线程的join方法
根据Unix环境高级编程中对进程控制一章的描述,当某个进程fork一个子进程后,该进程必须要调用wait等待子进程结束发送的sigchld信号,对子进程进行资源回收等相关工作,否则,子进程会成为僵死进程,被init收养。所以,在multiprocessing.Process实例化一个对象之后,该对象有必要调用join方法,因为在join方法中完成了对底层wait的处理
不过,调用该方法,要注意join的位置(threading模块有提到),是在每个子进程中阻塞还是在父进程中阻塞,如果在子进程中阻塞可能达不到并行处理的目的,所以要根据具体需求。而对于多线程来说,由于只有一个进程,所有子线程共享同一片内存,所以不是必须要进行join调用。
进程和线程的区别总结
线程与进程的区别可以归纳为以下4点: 1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。 2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。 3)调度和切换:线程上下文切换比进程上下文切换要快得多。 4)在多线程操作系统中,进程不是一个可执行的实体 通过漫画了解进程和线程
tets
上帝说要有光,于是便有了光;上帝说要有女人,于是便有了女人!
浙公网安备 33010602011771号