Python 进程,线程,协程
本节内容
-
进程和线程
- python GIL全局解释锁
-
线程语法
- 进程语法
1.进程与线程
1.1 什么是进程
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。
在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。
在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。正是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。
有了进程为什么还要线程?
进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:
-
进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
-
进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!
1.2 什么是线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
2.python GIL全局解释锁
Python GIL(Global Interpreter Lock)
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
上面的核心意思就是,无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行。
首先需要明确的一点是GIL
并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL
归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL
3.线程
线程的用法集合:
- start 线程准备就绪,等待CPU调度
- setName 为线程设置名称
- getName 获取线程名称
- setDaemon 设置为后台线程或前台线程(默认)
- 如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
- 如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
- join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
- run 线程被cpu调度后自动执行线程对象的run方法
3.1 threading模块
线程有2种调用方式,如下
直接调用
import threading def task(arg): # 定义每个线程要运行的函数 print(arg) for i in range(5): t = threading.Thread(target=task,args=(i,)) # 生成一个线程实例 t.start() # 启动线程 print(t.getName()) # 获取线程名 print('end')
继承调用
import threading class Mythread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.num = enumerate def run(self):# 定义每个线程要运行的函数 print('running on number:%s'%self.name) if __name__ == '__main__': for i in range(10): t = Mythread(i) t.start()
3.2 线程锁(Lock,RLock)
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁
- 同一时刻允许一个线程执行操作。
import threading import time def sayhi(num): lock.acquire() time.sleep(1) print('running %s'%num) lock.release() if __name__ == '__main__': lock = threading.Lock() # 普通的锁 for i in range(10): t = threading.Thread(target=sayhi,args=(i,)) # 创建进程 print(t.getName()) t.start() print('主程序'.center(20,'-'))
import threading import time def sayhi(num): lock.acquire() lock.acquire() time.sleep(1) print('running %s'%num) lock.release() lock.release() if __name__ == '__main__': lock = threading.RLock() # 递归锁 for i in range(10): t = threading.Thread(target=sayhi,args=(i,)) # 创建进程 print(t.getName()) t.start() print('主程序'.center(20,'-'))
3.2 信号量(Semaphore)
互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
import threading import time def sayhi(num): lock.acquire() # lock.acquire() time.sleep(1) print('running %s'%num) lock.release() # lock.release() if __name__ == '__main__': lock = threading.BoundedSemaphore(2) # 同时允许两个人操作数据 for i in range(10): t = threading.Thread(target=sayhi,args=(i,)) # 创建进程 print(t.getName()) t.start() print('主程序'.center(20,'-'))
3.3 事件(event)
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
import threading def sayhi(num): event.wait() # 锁住所有线程 print('running :%s'%num) if __name__ == '__main__': event = threading.Event() for i in range(10): t = threading.Thread(target=sayhi,args=(i,)) t.start() event.clear() string = input(':>>') if string == 'true': event.set() # 打开锁,允许所有线程通过
3.4 条件(Condition)
使得线程等待,只有满足某条件时,才释放n个线程
import threading import time lock = threading.Condition() # 指定解锁的进程个数 def take(arg): time.sleep(1) lock.acquire() # 锁住所有的线程 lock.wait() print(arg) lock.release() for i in range(10): t = threading.Thread(target=take,args=(i,)) t.start() while True: value = input('>>') lock.acquire() lock.notify(int(value)) lock.release()
3.5 Timer
定时器,指定n秒后执行某操作
import threading import time def sayhi(num): time.sleep(5) print('running :%s'%num) if __name__ == '__main__': for i in range(10): t = threading.Timer(1,sayhi,args=(i,)) t.start()
4 进程
multiprocessing是python的多进程管理包,和threading.Thread类似。
4.1 定义进程
import multiprocessing # 导入函数
import time
def foo(i): # 定义进程执行的方法
time.sleep(2)
print('say hi',i)
if __name__ == '__main__':
for i in range(10):
p = multiprocessing.Process(target=foo, args=(i,)) # 创建进程
p.start() # 执行进程
# p.join() # 等待进程执行完毕
进程的使用方法和线程的基本一致,可以参考上面说的,现在主要说说进程间通信。
4.2 进程间通信
在使用并发设计的时候最好尽可能的避免共享数据,尤其是在使用多进程的时候。 如果你真有需要 要共享数据, multiprocessing提供了两种方式。
不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:
# 该例可以看出进程间的是不能进行数据交换的
import multiprocessing
import time
li = []
def sayhi(num):
li.append(num)
print('hi',num)
if __name__ == '__main__':
for i in range(3):
p = multiprocessing.Process(target=sayhi,args=(i,))
p.start()
p.join()
time.sleep(1)
print(li)
# 输出 # hi 0 # hi 1 # hi 2 # []
第一种:
(1)multiprocessing,Array,Value
Value的使用参考http://www.cnblogs.com/aylin/p/5601969.html
数据可以用Value或Array存储在一个共享内存地图里,如下:
# 方法一,Array from multiprocessing import Process, Array # Array调用c语言底层,需要制定类型,列表元素必须统一。 temp = Array('i', [11, 22, 33, 44]) def Foo(i): temp[i] = 100 + i for item in temp: print(i, '----->', item) if __name__ == '__main__': for i in range(2): p = Process(target=Foo, args=(i,)) p.start()
第二种:
(2)multiprocessing,Manager
由Manager()返回的manager提供list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array类型的支持。
from multiprocessing import Process,Manager def task(num,li): # 把列表传给函数 li.append(num) print(li) if __name__ == '__main__': v = Manager().list() # 通过Manager生成列表 # v = Manager().dict() for i in range(10): p = Process(target=task,args=(i,v,)) p.start() p.join() # 串行执行
线程池,进程池,还有协程下节讲。