首页 > 代码库 > 40.python全栈之路:进程与线程
40.python全栈之路:进程与线程
进程与线程
1.什么是进程,什么是线程?
进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。每一个进程都有一个自己的地址空间,即进程空间或(虚空间)。进程空间的大小 只与处理机的位数有关,一个 16 位长处理机的进程空间大小为 216 ,而32 位处理机的进程空间大小为 232 。进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。
线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。
2.进程与线程的区别?
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对
其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一
个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但
对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
3.全局解释器锁(GIL)
在进程上加一把锁,为了锁住多个线程,只要多个线程中的其中一个去访问了CPU,那么久锁住进程的资源,不允许其他线程访问同一资源
4.使用多线程及其其常用方法
setDaemon(True):主线程不等待子线程
join(n):主线程最多等待子线程n秒
import threading import time def f1(a1, a2): time.sleep(5) print(‘666‘) t1 = threading.Thread(target=f1, args=(123, 111)) t1.start() t2 = threading.Thread(target=f1, args=(123, 111)) t2.start() t3 = threading.Thread(target=f1, args=(123, 111)) t3.start() ‘‘‘ 程序等待5秒后,3个线程同时输出666 实现并发 ‘‘‘
5.线程锁
import threading import time globals_num = 0 lock = threading.RLock() # 实例化一个锁 def Func(): lock.acquire() # 获得锁,锁定当前访问资源 global globals_num globals_num += 1 time.sleep(1) print(globals_num) lock.release() # 释放锁,释放当前访问的资源 for i in range(10): t = threading.Thread(target=Func) t.start() ‘‘‘ 休息一秒 输出一个,因为资源被锁定, 只能等资源被释放了才能使用全部变量 不能像上一个例子一样并发 ‘‘‘
6.主线程控制子线程的方法(线程间通讯)
Event是线程间通信最间的机制之一:一个线程发送一个event信号,其他的线程则等待这个信号。
用于主线程控制其他线程的执行。 Events 管理一个flag,这个flag可以使用set()设置成True或者
使用clear()重置为False,wait()则用于阻塞,在flag为True之前。flag默认为False。
Event.wait([timeout]) : 堵塞线程,直到Event对象内部标识位被设为true或超时(如果提供了参数timeout)。
Event.set() :将标识位设为ture。
Event.clear() : 将标识伴设为false。
Event.isSet() :判断标识位是否为ture。
import threading def do(event): # 循环生成了10个线程 print(‘start‘) event.wait() # 线程产生阻塞,默认为flase,等待标志位为true,线程才继续执行 print(‘execute‘) event_obj = threading.Event() for i in range(10): t = threading.Thread(target=do, args=(event_obj,)) t.start() event_obj.clear() #falge设置为false inp = input(‘input:‘) if inp == ‘true‘: event_obj.set() # flage设置为true
7.创建进程并使用
#创建进程 import multiprocessing import time # 主进程的主线程创建了两个子进程,再由子进程的线程去执行f1方法 def f1(a1): time.sleep(2) print(a1) if __name__ == ‘__main__‘: t1 = multiprocessing.Process(target=f1, args=(11,)) # t1.daemon = True t1.start() # 运行进程 在Windows下需要写在__name__ == ‘__main__‘下 # t1.join() t2 = multiprocessing.Process(target=f1, args=(11,)) # t2.daemon = True t2.start() print(‘end‘)
8.进程与线程在资源共享问题上的区别
进程:
# 进程不能共享数据,拷贝原数据并单独使用 import multiprocessing li = [] def foo(i): li.append(i) print(‘say hi‘,li) if __name__ == ‘__main__‘: for i in range(10): p = multiprocessing.Process(target=foo, args=(i,)) p.start() ‘‘‘ say hi [1] say hi [2] say hi [0] say hi [3] say hi [4] say hi [5] say hi [6] say hi [7] say hi [8] say hi [9] ‘‘‘
线程:
#线程与线程之间的内存是共享的 import threading 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] ‘‘‘
9.进程间内存共享方法(默认不共享)
# manage.dict()共享数据 特殊的字典,支持进程之间共享数据 from multiprocessing import Process, Manager def Foo(i, dic): dic[i] = 100 + i for k, v in dic.items(): print(k, v) if __name__ == ‘__main__‘: manage = Manager() dic = manage.dict() # dic = {} for i in range(2): p = Process(target=Foo, args=(i, dic)) p.start() p.join() # 避免主进程关闭后,子进程无法连接主进程,无法拿到数据 print(dic) ‘‘‘ 用manage.dict,数据共享 0 100 第一次只有1个数据 0 100 第二次有两个数据哟 1 101 {0: 100, 1: 101} ‘‘‘ ‘‘‘ 用普通字典,数据不共享 0 100 1 101 {} ‘‘‘
10.进程池与线程池
进程池:python内部提供
# python提供了进程池 from multiprocessing import Pool import time def Foo(i): time.sleep(2) print(i) if __name__ == ‘__main__‘: pool = Pool(5) # 最大能装5个进程,用一个加一个,不事先全部创完 # 申请进程,执行完后放回进程池 # pool.apply(Foo,(1,)) # pool.apply_async(func=Foo, args=(i,)).get() 执行完后,告诉我们一下,再放回进程池 # 执行完Foo方法后,将Foo方法的返回值,赋值给Bar方法的参数 # Bar方法,我们称之为回调函数 for i in range(40): # pool.apply(Foo,(i,)) apply申请的时候,一个一个申请并执行 pool.apply_async(func=Foo, args=(i,)) # 并发进行,还能设置回调函数 print(‘666666666666‘) pool.close() pool.join()
low版线程池:缺点在于,线程执行任务后,不能被重复使用,会等待python销毁它
import queue import threading import time class ThreadPool(object): def __init__(self, max_num=20): self.queue = queue.Queue(max_num) # 创建一个最大长度为20的队列 for i in range(max_num): self.queue.put(threading.Thread) # 传递类名,有20个类名的队列,都指向同一个类 def get_thread(self): return self.queue.get() # 当队列为空的时候,get则等待 def add_thread(self): self.queue.put(threading.Thread) def func(pool, a1): time.sleep(1) print(a1) pool.add_thread() p = ThreadPool(10) for i in range(100): thread = p.get_thread() # ret 等价于 threading.Thread,是个类名 t = thread(target=func, args=(p, i)) # 创建一个线程,将线程池对象传入func中,在func结束的时候,再将该线程重新放入线程池 t.start() # 启动线程 # 在队列中放类,线程放在内存在 等待销毁,这样是不合理的,我们可以优化,线程继续做第二件事情 # 在队列里面放任务,让线程不停的取任务,重复利用线程
40.python全栈之路:进程与线程