本章内容
1、线程、进程介绍
2、线程的基本使用
3、setDeamon、join
4、线程锁
5、线程池
6、信号量(Semaphore)
7、时间(event)
8、条件(Condition)
9、Timer
10、queue队列
11、生产消费者模型
12、进程
13、进程间的通讯
14、进程池
15、协程
16、Greenlet
17、Gevent
线程、进程介绍
1、什么是进程、什么是线程?
2、为什么要有进程?
让我们下面来解答这些答案
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程的实际运算单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中科院并发多个线程,每条线程并行执行不同的任务
进程一个程序所有资源的集合,一个程序的实例,如果进行想要执行必须有一个线程,本身不能执行
线程与进程的区别
1、程序工作的最小单元是线程,是一个指令集
2、一个进程中的所有线程共享同一块内存空间,可以做直接交流,而进程的内存是独立的,如果要通信必须通过一个中间代理来实现
3、一个线程可以控制和操作同一个进程里的其他线程,但进程只能操作子进程
4、一个线程的修改可能影响到其他其他线程,一个进程的修改不会影响到其他进程
5、每个应用程序 --->至少有一个进程 --->至少有一个线程
6、应用场景:
IO密集型:用线程
计算秘籍型:进程
python独有的GIL,全局解释器锁。
保证同一个进程中只有一个线程同时被调度(如果所有线程同时被调用,一个变量可能会被改变多次)。 这是被人诟病的点,但是去掉后,性能会更低,所以还存在,正式由于GIL的存在,所以与cpu打交道时,一个cpu只会处理一个线程。
python是假线程,只是cpu上下文切换的快,所以像多线程。
线程的基本使用
import threading
import time
def task(arg,at):
time.sleep(1)
print(arg,at)
for i in range(10):
t = threading.Thread(target=task,args=[i,12])#target跟的是函数,args跟可迭代的对象,
t.start()
print("main done!!!")
#每个t都是一个线程,这个python程序是主线程。
import threading import time class MyThread(threading.Thread): def __init__(self,n): super(MyThread,self).__init__() self.n = n def run(self): #这里必须得使用run,名字不可以随便改 print('run task',self.n) t1 = MyThread('1') t2 = MyThread('2') t1.start() t2.start()
更多方法:
- start #线程准备就绪,等待CPU调度
- setName #为线程设置名称
- getName #获取线程名称
- setDaemon #设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 - join #逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
- run #线程被cpu调度后自动执行线程对象的run方法
setDeamon、join
import threading
import time
def task(arg,at):
time.sleep(1)
print(arg,at)
for i in range(10):
t = threading.Thread(target=task,args=[i,12]) #target跟的是函数,args跟可迭代的对象
#t.setDaemon(True) #设置为子程序为守护线程,主线程停了,子线程就不会执行
t.start()
#t.join() #使线程依次的进行,一个完了再另外一个,相当于wait
#t.join(1) #等待最大的时间
print("main done!!!",threading.current_thread()) #查看当前线程的类型,主线程or子线程
print(threading.active_count()) #查看活跃的线程
如果我for循环启动50个线程,然后每个线程都想用join那该如何?
t_obj = [] for i in range(50): t = threading.Thread(target=run,args=(i,)) t.start() t_obj.append(t) #把t对象放到列表中 for t in t_obj: t.join() #把t对象与join关联起来
线程锁
告知线程,我要修改这个数据,其线程不要动!python2中需要锁,python3中不需要
比如,淘宝商店的商品计数,如果100个并发过来同时要买商品,这个商品的计数怎么往下减呢,肯定不能同时做修改,需要一个一个的来,这里就用到了锁的概念,意思是虽然是并发来的,但是每个进程都放一把锁,每次只能一个来修改。
#!/usr/bin/env python # -*- coding:utf-8 -*- import threading import time gl_num = 0 def show(arg): global gl_num time.sleep(1) gl_num +=1 print gl_num for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start() print 'main thread stop' #运行结果 MacBook-Air ~/tmp $ python aa.py main thread stop 123 56 78 9 4 10 MacBook-Air ~/tmp $ python aa.py main thread stop 13245 7 8 9 6 10 #在python2中会出现这种错乱的情况,且每次的情况都不一样
#!/usr/bin/env python #coding:utf-8 import threading import time gl_num = 0 lock = threading.Lock() def Func(): lock.acquire() global gl_num gl_num +=1 time.sleep(1) print gl_num lock.release() for i in range(10): t = threading.Thread(target=Func) t.start()
递归锁:
lock.threading.RLock()
递归锁的意思就是,锁了两次,然后再解锁两次才能释放
多人锁:
可以支持批量操作,适用于可批量操作的任务
事件锁
当输入 1 时,才会解锁,继续执行。
肆意妄为锁
想执行几个就执行几个。
执行结果就是输入数字几就会执行几个线程
线程池
python2,中没有线程池,但可以去自己写个,但是比较麻烦,python3有,且简单
下面是个利用线程池进行request 请求的小例子
信号量(Semaphore)
互斥锁(线程锁) 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
import threading,time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s" %n) semaphore.release() if __name__ == '__main__': num= 0 semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行 for i in range(20): t = threading.Thread(target=run,args=(i,)) t.start() #运行结果是,线程5个5个的向外输出,同时5个进行
事件(event)
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
import threading,time import random def light(): if not event.isSet(): event.set() #wait就不阻塞 #绿灯状态 count = 0 while True: if count < 10: print('