本文共 6144 字,大约阅读时间需要 20 分钟。
调度算法 时间片轮流 优先级调度 进程: 1、导入os模块 2、ret=os.fork():创建一个子进程 3、分为两种情况:ret==0: ret!=0: 例: import os # 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以,我们不用folk()了解即可 pid = os.fork() print(pid)//在父进程打印的是子进程的id if pid == 0: print('--子进程--%d---'%os.getpid()) elif pid>0: print('--父进程--%d-%d-'%(os.getpid(),os.getppid())) else: print('调用失败') print("父子进程都执行的代码") 结果: 20570 --父进程--20569-- 0 ---子进程---20570-20569- 普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次, 因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。 子进程永远返回0,而父进程返回子进程的ID。 这样做的理由是,一个父进程可以fork出很多子进程, 所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。 注意: 1、父子进程的先后顺序,父进程不会因为子进程没有结束,而不结束 2、多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响 3、父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法 4、fork函数,只在Unix/Linux/Mac上运行,windows不可以,以后不用fock() multiprocessing(重点):linux和windows都可以执行 multiprocessing模块提供了一个Process类来代表一个进程对象 例: from multiprocessing import Process import os # 子进程要执行的代码 def run_proc(name): print('子进程运行中,name= %s ,pid=%d...' % (name, os.getpid())) if __name__=='__main__': print('父进程 %d.' % os.getpid()) p = Process(target=run_proc, args=('test',)) print('子进程将要执行') p.start() p.join() print('子进程已结束') 创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。 join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。 注意: 1、主进程会等待子进程完成后,在结束进程 Process([group [, target [, name [, args [, kwargs]]]]]) target:表示这个进程实例所调用对象; args:表示调用对象的位置参数元组; kwargs:表示调用对象的关键字参数字典; name:为当前进程实例的别名; group:大多数情况下用不到; Process类常用方法: is_alive():判断进程实例是否还在执行; join([timeout]):是否等待进程实例执行结束,或等待多少秒; start():启动进程实例(创建子进程); run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法; terminate():不管任务是否完成,立即终止; Process类常用属性: name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数; pid:当前进程实例的PID值; 进程池Pool: from multiprocessing import Pool #第一步导入Pool方法 import os,time,random def worker(msg): t_start = time.time() print("%s开始执行,进程号为%d"%(msg,os.getpid())) #random.random()随机生成0~1之间的浮点数 time.sleep(random.random()*2) t_stop = time.time() print(msg,"执行完毕,耗时%0.2f"%(t_stop-t_start)) po=Pool(3) #定义一个进程池,最大进程数3 for i in range(0,10): #Pool.apply_async(要调用的目标,(传递给目标的参数元祖,)) #每次循环将会用空闲出来的子进程去调用目标 po.apply_async(worker,(i,)) #注意若元组只有一个值时,要加个逗号 print("----start----") po.close() #关闭进程池,关闭后po不再接收新的请求 po.join() #,必须放在close语句之后,默认主进程完成后结束,但执行该方法后,会等待po中所有子进程执行完成 print("-----end-----") 多个进程创建比较: 一般使用进程池,不使用folk() 进程间通信-Queue:(重点) 可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序 Queue.qsize():返回当前队列包含的消息数量; Queue.empty():如果队列为空,返回True,反之False ; Queue.full():如果队列满了,返回True,反之False; Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True, 消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止, 如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常; Queue.get_nowait():相当Queue.get(False); Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True; Queue.put_nowait(item):相当Queue.put(item, False); 例: from multiprocessing import Process, Queue import os, time, random # 写数据进程执行的代码: def write(q): for value in ['A', 'B', 'C']: print 'Put %s to queue...' % value q.put(value) time.sleep(random.random()) # 读数据进程执行的代码: def read(q): while True: if not q.empty(): value = q.get(True) print 'Get %s from queue.' % value time.sleep(random.random()) else: break if __name__=='__main__': # 父进程创建Queue,并传给各个子进程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 启动子进程pw,写入: pw.start() # 等待pw结束: pw.join() # 启动子进程pr,读取: pr.start() pr.join() # pr进程里是死循环,无法等待其结束,只能强行终止: print '' print '所有数据都写入并且读完' 如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(), 即q=Manager.Queue()来创建队列 多线程-threading 1、使用threading模块 from threading import Thread t=Thread(target=方法) t.start()#启动线程 2. 主线程会等待所有的子线程结束后才结束 3、查看线程数量:length = len(threading.enumerate()) 4、使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法 5、多线程程序的执行顺序是不确定的。当执行到sleep语句时,线程将被阻塞(Blocked), 到sleep结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。 6、当线程的run()方法结束时该线程完成。 7、在一个进程内的所有线程共享全局变量,能够在不适用其他方式的前提下完成多线程之间的数据共享(这点要比多进程要好) 缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全) 8、列表当做实参传递到线程中----------->t2 = Thread(target=work2, args=(g_nums,)) 9、在多线程开发中,全局变量是多个线程都共享的数据,而局部变量等是各自线程的,是非共享的 进程与线程区别: 进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位 线程同步: 同步就是协同步调,按预定的先后次序进行运行 使用互斥锁来实现 互斥锁: 当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。 理解: 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改; 直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。 #创建锁 mutex = threading.Lock() #锁定 mutex.acquire([blocking])--->blocking为True,则当前线程会堵塞,直到获取到这个锁为止,为False,则当前线程不会堵塞,默认True 返回类型:True/False #释放 mutex.release() ThreadLocal 用threading.local()创建一个全局变量 一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。 ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题 例: import threading # 创建全局ThreadLocal对象: local_school = threading.local() def process_student(): # 获取当前线程关联的student: std = local_school.student print('Hello, %s (in %s)' % (std, threading.current_thread().name)) def process_thread(name): # 绑定ThreadLocal的student: local_school.student = name process_student() t1 = threading.Thread(target= process_thread, args=('dongGe',), name='Thread-A') t2 = threading.Thread(target= process_thread, args=('老王',), name='Thread-B') t1.start() t2.start() t1.join() t2.join() 执行结果: Hello, dongGe (in Thread-A) Hello, 老王 (in Thread-B) 异步(重点) 同步调用就是你 喊 你朋友吃饭 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你们一起去 异步调用就是你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你就去做别的了。 例: from multiprocessing import Pool import time import os def test(): print("---进程池中的进程---pid=%d,ppid=%d--"%(os.getpid(),os.getppid())) for i in range(3): print("----%d---"%i) time.sleep(1) return "hahah" def test2(args): print("---callback func--pid=%d"%os.getpid()) print("---callback func--args=%s"%args) pool = Pool(3) pool.apply_async(func=test,callback=test2) //callback为回调函数,当子进程完成后,系统告诉主进程去做回调函数的事 while True: sleep(1) print("----主进程-pid=%d----"%os.getpid()) 运行结果: ---进程池中的进程---pid=9401,ppid=9400-- ----0--- ----1--- ----2--- ----主进程-pid=9400---- ----主进程-pid=9400---- ----主进程-pid=9400---- ----主进程-pid=9400---- //停一会先做回调函数的事,做完在做自己的事 ---callback func--pid=9400 ---callback func--args=hahah ----主进程-pid=9400---- ----主进程-pid=9400---- ... 1、callback为回调函数,当子进程完成后,系统告诉主进程去做回调函数的事 2、子进程返回的值给回调函数 GIL问题: Python多线程是假的需要依赖GIL 解决方案: 1、关键部分使用c语言或者其他语言写 2、使用多进程代替多线程 Python多进程是真的转载地址:http://waobi.baihongyu.com/