第二十五篇、进程和线程
一、进程与线程之间的关系
线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。
1 优点:同时利用多个cpu,能够同时进行多个操作 2 缺点:耗费资源(重新开辟内存空间)
1 优点:共享内存,IO操作时候,创造并发操作 2 缺点:抢占资源
进程不是越多越好,cpu个数=进程个数
线程也不是越多越好,具体案例具体分析,请求上下文切换耗时
1 1、计算机执行程序任务的最小单元是线程 2 2、所有的操作都不是利用cpu的,IO操作不是利用cpu的 3 所以 4 IO密集型(不用cpu) 5 用多线程 6 计算密集型(用cpu) 7 用多进程
二、使用线程
1、
import threading def f0(): pass def f1(a1,a2): time.sleep(10) f0() t1 = threading.Thread(target=f1,args=(123,111,)) t1.start() t1.join() t = threading.Thread(target=f1,args=(123,111,)) t.setDaemon(True) t.start() t = threading.Thread(target=f1,args=(123,111,)) t.setDaemon(True) t.start()
以上创建的线程叫做3个子线程
上面三个线程执行完毕之后就夯住了,然后等待10秒结束
2、
上面的setDeamon如果为True的时候就可以设置主线程是否为后台线程。如果是,那么主线程执行完毕,就不会等待子线程是否执行完毕,直接结束
主线程等待,子线程执行
3、
t.join():这个线程执行完毕之后再往下执行,如果里面有参数n,表示这个线程最多等待n秒就往下执行,如果n大于主线程时间a,那么就最多等待a秒
1 t.start() : 激活线程, 2 3 t.getName() : 获取线程的名称 4 5 t.setName() : 设置线程的名称 6 7 t.name : 获取或设置线程的名称 8 9 t.is_alive() : 判断线程是否为激活状态 10 11 t.isAlive() :判断线程是否为激活状态 12 13 t.setDaemon() 设置为后台线程或前台线程(默认:False);通过一个布尔值设置线程是否为守护线程,必须在执行start()方法之后才可以使用。如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 14 15 t.isDaemon() : 判断是否为守护线程 16 17 t.ident :获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None。 18 19 t.join() :逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义 20 21 t.run() :线程被cpu调度后自动执行线程对象的run方法
三、线程锁threading.RLock和threading.Lock
我们使用线程对数据进行操作的时候,如果多个线程同时修改某个数据,可能会出现不可预料的结果,为了保证数据的准确性,引入了锁的概念
#!/usr/bin/env python #-*- coding:utf-8 -*- import threading import time glo_num = 0 lock = threading.RLock() def func(): lock.acquire() #获的锁 global glo_num glo_num +=1 time.sleep(1) print(glo_num) lock.release()#释放锁 for i in range(10): t = threading.Thread(target=func) t.start() 结果 1 2 3 4 5 6 7 8 9 10
threading.RLock和threading.Lock 的区别
RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。
所以
lock不好,建议用Rlock
四、线程事件threading.Event
Event是线程间通信最间的机制之一:一个线程发送一个event信号,其他的线程则等待这个信号。用于主线程控制其他线程的执行。 Events 管理一个flag,这个flag可以使用set()设置成True或者使用clear()重置为False,wait()则用于阻塞,在flag为True之前。flag默认为False。
#!/usr/bin/env python #-*- coding:utf-8 -*- import threading def do(event): print("start") event.wait() #红灯 print("execute") event_obj = threading.Event() for i in range(10): #相当于汽车 t = threading.Thread(target=do,args=(event_obj,)) t.start() event_obj.clear() #让灯变红 inp = input("input:") if inp == "True": event_obj.set() #让等边绿 当用户输入true的时候,才能让车路过
Event.wait([timeout]) : 堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)。 Event.set() :将标识位设为Ture Event.clear() : 将标识伴设为False。 Event.isSet() :判断标识位是否为Ture。
五、队列和生产者消费者模式
1 Queue 就是对队列,它是线程安全的 2 举例来说,我们去肯德基吃饭。厨房是给我们做饭的地方,前台负责把厨房做好的饭卖给顾客,顾客则去前台领取做好的饭。这里的前台就相当于我们的队列。(先进先出) 3 这个模型也叫生产者-消费者模型。 4 python2中的queue是大写开头,3里面是小写
#!/usr/bin/env python import Queue import threading message = Queue.Queue(10) def producer(i): print(“aa”) #while True: message.put(i) def consumer(i): print(“aa”) #while True: msg = message.get() for i in range(12): t = threading.Thread(target=producer, args=(i,)) t.start() for i in range(10): t = threading.Thread(target=consumer, args=(i,)) t.start()
import queue q = queue.Queue(maxsize=0) # 构造一个先进显出队列,maxsize指定队列长度,为0 时,表示队列长度无限制。 q.join() # 等到队列为kong的时候,在执行别的操作 q.qsize() # 返回队列的大小 (不可靠) q.empty() # 当队列为空的时候,返回True 否则返回False (不可靠) q.full() # 当队列满的时候,返回True,否则返回False (不可靠) q.put(item, block=True, timeout=None) # 将item放入Queue尾部,item必须存在,可以参数block默认为True,表示当队列满时,会等待队列给出可用位置, 为False时为非阻塞,此时如果队列已满,会引发queue.Full 异常。 可选参数timeout,表示 会阻塞设置的时间,过后, 如果队列无法给出放入item的位置,则引发 queue.Full 异常 q.get(block=True, timeout=None) # 移除并返回队列头部的一个值,可选参数block默认为True,表示获取值的时候,如果队列为空,则阻塞,为False时,不阻塞, 若此时队列为空,则引发 queue.Empty异常。 可选参数timeout,表示会阻塞设置的时候,过后,如果队列为空,则引发Empty异常。 q.put_nowait(item) # 等效于 put(item,block=False) q.get_nowait() # 等效于 get(item,block=False)
方法中的put(往队列里面放值)
get方法(从队列里面取值)
队列的例子:12306网站就是内存级别的队列
还有更好的队列,后面会说到
六、multiprocessing模块
multiprocessing是python的多进程管理包,和threading.Thread类似。直接从侧面用subprocesses替换线程使用GIL的方式,由于这一点,multiprocessing模块可以让程序员在给定的机器上充分的利用CPU。
在multiprocessing中,通过创建Process对象生成进程,然后调用它的start()方法
from multiprocessing import Process def f(name): print('hello', name) if __name__ == '__main__': p = Process(target=f, args=('bob',)) p.start() p.join()
七、进程
import multiprocessing import time def f1(a1): time.sleep(2) print(a1) if __name__ == “__main__”: t = multiprocessing.Process(target=f1,args=(11,)) t.deamon = True t.start() print (“end”) 上面的是两个子进程
创建进程windows里面运行不了,但是把进程写在if __name__ == “__main__”:里面,但是总不可能永远写在这里面,所以要写在linux中运行
2、daemon和线程的一样,是否为后台进程True 为后台进程,即主线程执行完毕之后就不再执行其他 3、join和线程类似,join这个进程执行完毕之后才往下执行
4、进程和进程之间的数据是不能够共享的,如果一个程序能够随便进入另一个进程里面读取数据会造成混乱 如某某管家
#!/usr/bin/env python #-*- coding:utf-8 -*- from multiprocessing import Process import threading import time li = [] def foo(i): li.append(i) print("say hi",li) if __name__ == "__main__": for i in range(10): p = Process(target=foo,args=(i,)) p.start() 结果: say hi [0] say hi [1] say hi [2] say hi [3] say hi [4] say hi [5] say hi [6] say hi [7] say hi [9] say hi [8] 上面一个进程创建一个新的列表
#!/usr/bin/env python #-*- coding:utf-8 -*- from multiprocessing import Process import threading import time li = [] def foo(i): li.append(i) print("say hi",li) if __name__ == "__main__": for i in range(10): p = threading.Thread(target=foo,args=(i,)) p.start() say hi [0] say hi [0, 1] say hi [0, 1, 2] say hi [0, 1, 2, 3] say hi [0, 1, 2, 3, 4] say hi [0, 1, 2, 3, 4, 5] say hi [0, 1, 2, 3, 4, 5, 6] say hi [0, 1, 2, 3, 4, 5, 6, 7] say hi [0, 1, 2, 3, 4, 5, 6, 7, 8] say hi [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5、一个程序之间的数据也能进行共享
需要用到如下方法
1、Array方法
数组在定义的时候必须要制定长度。或者元素 数组里面必须是统一的数据类型 from multiprocessing import Process,Array tmp = Array(“i”,[11,22]) 里面的参数I 指定数据类型
from multiprocessing import Process,Array temp = Array('i', [11,22,33,44]) def Foo(i): temp[i] = 100+i for item in temp: print i,'----->',item for i in range(2): p = Process(target=Foo,args=(i,)) p.start()
2、manage.dict()共享数据
m = Manager() m.dict()
from multiprocessing import Process,Manager def Foo(i,dic): dic[i] = 100+i print(len(dic)) if __name__ == '__main__': manage = Manager() dic = manage.dict() for i in range(2): p = Process(target=Foo,args=(i,dic,)) p.start() p.join()
'c': ctypes.c_char, 'u': ctypes.c_wchar, 'b': ctypes.c_byte, 'B': ctypes.c_ubyte, 'h': ctypes.c_short, 'H': ctypes.c_ushort, 'i': ctypes.c_int, 'I': ctypes.c_uint, 'l': ctypes.c_long, 'L': ctypes.c_ulong, 'f': ctypes.c_float, 'd': ctypes.c_double