引入进程和线程的概念及区别
threading模块提供的类:
Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local。
1.什么是进程
计算机程序只不过是磁盘中可执行的二进制(或其他类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。
进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间、内存、数据栈及其它记录其运行轨迹的辅助数据。
操作系统管理在其上运行的所有进程,并为这些进程公平的分配时间,进程也可以通过fork和spawn操作来完成其它的任务。
不过各个进程有自己的内存空间、数据栈等,所以只能使用进程间通讯,而不能直接共享信息。
2.线程的基本概念
线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
3、线程和进程的关系以及区别?
** 进程和线程的关系:**
-
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
-
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
-
(3)处理机分给线程,即真正在处理机上运行的是线程
-
(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
进程与线程的区别:
-
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
-
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
-
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
-
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
1 多进程创建方式
可以归纳为三种:fork,multiprocessing以及进程池Pool。
(1) fork方式
1 import os 2 3 # 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以 4 pid = os.fork() 5 6 if pid == 0: 7 print('哈哈1') 8 else: 9 print('哈哈2')
注意:fork()函数只能在Unix/Linux/Mac上面运行,不可以在Windows上面运行。
说明:
- 程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中
- 然后父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的 id号
在Unix/Linux操作系统中,提供了一个fork()系统函数,它非常特殊。
普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID。
这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。我们可以通过os.getpid()获取当前进程ID,通过os.getppid()获取父进程ID。
那么,父子进程之间的执行有顺序吗?
答案是没有!这完全取决于操作系统的调度算法。
而多次fork()就会产生一个树的结构:
(2)multiprocessing方式
如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?当然可以!由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。
1 import os 2 import time 3 4 from multiprocessing import Process 5 6 def run_proc(name): 7 print('子进程运行中,name%s,pin=%d...'%(name,os.getpid())) 8 9 time.sleep(10) 10 print('子进程已经结束') 11 12 if __name__=='__main__': 13 print('父进程%d.'%os.getpid()) 14 p=Process(target=run_proc,args=('test',)) 15 print('子进程将要执行') 16 p.start()
从结果我们看出,只要通过start()开启了子进程之后,主进程会等待子进程执行完才结束!
Process的语法结构如下:
import os import time from multiprocessing import Process class MyProcess(Process): def __init__(self,name): Process.__init__(self) self.name=name def run(self): print('子进程运行中,name= %s ,pid=%d...' % (self.name, os.getpid())) import time time.sleep(10) print('子进程已结束') if __name__=="__main__": my=MyProcess('test') my.start()
(3)Pool方式
#coding=utf-8 from multiprocessing import Pool import os, time, random def worker(msg): print("%s开始执行,进程号为%d"%(msg, os.getpid())) time.sleep(1) print "%s执行完毕"%(msg) if __name__ == '__main__': po = Pool(3) # 定义一个进程池,最大进程数3 for i in range(10): # Pool.apply_async(要调用的目标,(传递给目标的参数元祖,)) # 每次循环将会用空闲出来的子进程去调用目标 po.apply_async(worker, (i,)) print("----start----") po.close() # 关闭进程池,关闭后po不再接收新的请求 po.join() # 等待po中所有子进程执行完成,必须放在close语句之后 print("-----end-----")
实现一个多进程下的文件夹复制功能:
1 #coding=utf-8 2 3 import os 4 from multiprocessing import Pool 5 6 7 def copyFileTask(name, oldFolderName, newFolderName): 8 # 完成copy一个文件的功能 9 fr = open(oldFolderName+"/"+name, 'rb+') 10 fw = open(newFolderName+"/"+name, 'wb+') 11 12 str = fr.read(1024 * 5) 13 while (str != ''): 14 fw.write(str) 15 str = fr.read(1024 * 5) 16 17 fr.close() 18 fw.close() 19 20 21 def main(): 22 # 获取要copy的文件夹名字 23 oldFolderName = raw_input('请输入文件夹名字:') 24 # 创建一个文件夹 25 newFolderName = oldFolderName+'-复件'.decode('utf-8').encode('gbk') 26 os.mkdir(newFolderName) 27 28 #获取old文件夹里面所有文件的名字 29 fileNames = os.listdir(oldFolderName) 30 31 #使用多进程的方式copy原文件夹所有内容到新的文件夹中 32 pool = Pool(5) 33 for name in fileNames: 34 pool.apply_async(copyFileTask, (name, oldFolderName, newFolderName)) 35 36 pool.close() 37 pool.join() 38 39 if __name__ == '__main__': 40 main()