进程和线程
什么是线程(thread)什么是进程
线程:操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中的实际运作单位。是一串指令的集合
一个线程指的是进程中一个单一顺序的控制流,一个进程是中可以并发多个线程,每个线程并行执行不同的任务
进程:以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等,对各种资源管理的集合,就可以称为进程
进程要操作cpu,必须先创建一个线程,所有在同一个进程里的线程是共享同一块内存空间的
注意:1.进程本身不能够执行
2.进程和线程不能比较谁快谁慢,两个没有可比性,进程是资源的集合,线程是真正执行任务的,进程要执行任务也要通过线程
3.启动一个线程比启动一个进程快
进程和线程的区别
1.线程共享内存空间,进程的内存是独立的
2.thread have direct access to data segment of its process;process have their own copy of the data segment of the parent process
3.同一个进程之间线程可以互相交流,两个进程想要通信,必须通过一个中间代理来实现
4.创建一个线程很简单,创建一个新进程需要对其父进程做一次克隆
5.一个线程可以控制和操作同一个进程里的其他的线程,但是进程只能操作子进程
6.改变一个主线程可能会影响到其他线程的运行,对父进程的修改不会影响到子进程
Python GIL(全局解释器锁)
在python中无论你启用多少个线程,你有多少个cpu,在python执行的时候都会在同一时刻只准许一个线程运行
原因:python的线程是调用操作系统的原生线程
线程锁:
1 import threading 2 import time 3 def run(n): 4 lock.acquire() 5 global num 6 num += 1 7 #time.sleep(1)在上锁的情况不要使用sleep,不然会等50s才会完成 8 lock.release() 9 lock = threading.Lock() 10 num = 0 11 start_time = time.time() 12 t_objs = [] 13 for i in range(50): 14 t = threading.Thread(target = run,args = ("t-%s" %i,)) 15 t.start() 16 t_objs.append(t) 17 for t in t_objs: 18 t.join() 19 print("-----all threads has finished....",threading.current_thread(),threading.active_count()) 20 21 print("num = ",num)
给线程上锁,使程序变成串行,保证num在+1的时候能够准确无误,不然可能会导致,一个线程正在执行+1还没有结束,另外一个线程也开始+1,最后达不到准确结果
递归锁:在一个大锁中包含一个小锁
场景:学校放学,学生离校了,突然发现自己的文具盒落在了教师中,学校每次只准许一个人进学校(其他东西丢了就是这个人干的),进教室拿的时候也要开教室门(防止有其他人在学校没有走),最后拿到文具盒,离开学校。这个期间学校大门相当于一把锁,教室的门相当于另一把锁。保证了整个过程没有其他人的干扰
1 import threading,time 2 3 def run1(): 4 print("grab the first part data") 5 lock.acquire() 6 global num 7 num += 1 8 lock.release() 9 return num 10 11 def run2(): 12 print("grab the second part data") 13 lock.acquire() 14 global num2 15 num2 += 1 16 lock.release() 17 return num2 18 19 def run3(): 20 lock.acquire() 21 res = run1() 22 print('------between run1 and run2') 23 res2 = run2() 24 lock.release() 25 print(res,res2) 26 27 num,num2 = 0,0 28 lock = threading.RLock() 29 for i in range(10): 30 t = threading.Thread(target=run3) 31 t.start() 32 33 while threading.active_count() != 1: 34 print(threading.active_count()) 35 else: 36 print("----all threads done---") 37 print(num,num2)
相当于先进入run3这个大门,然后在进入run1和run2这个两个小门,然后进行num1和num2的加1,保证了每个线程的执行都是串行的
Python threading模块
线程有2中调用方式
直接调用:
1 import threading 2 import time 3 4 def run(n): 5 print("task ",n) 6 time.sleep(2) 7 8 t1 = threading.Thread(target = run,args = ("t1",)) 9 t2 = threading.Thread(target = run,args = ("t2",)) 10 11 t1.start() 12 t2.start()
运行结果:一下子出来两个结果,但是程序还会等待2s才会结束,一共两秒,因为他们是并行的
继承式调用:
1 import threading 2 import time 3 4 #用类的方式启动线程 5 class MyThread(threading.Thread): 6 def __init__(self,n): 7 super(MyThread,self).__init__() 8 self.n = n 9 def run(self): 10 print("running task ",self.n) 11 12 t1 = MyThread("t1") 13 t2 = MyThread("t2") 14 t1.start() 15 t2.start()
上面只启动了2个线程,我们下面启动50个线程:
1 import threading 2 import time 3 4 def run(n): 5 print("task ",n) 6 time.sleep(2) 7 8 start_time = time.time() 9 for i in range(50): 10 11 t = threading.Thread(target = run,args = ("t-%s" %i,)) 12 t.start() 13 print("cost : ",time.time()-start_time)
运行结果:
问题:创建了50个进程之后只用了这点时间,主程序没有等其他的子线程就往下走
原因:是多线程的原因,一个程序至少有一个线程,程序本身就是一个线程,主线程,主线程启动了子线程之后,子线程就和主线程没有关系了,两个互不影响
解决方法:join()方法,等子线程运行完成之后,主程序才往下走
import threading import time def run(n): print("task ",n) time.sleep(2) print("task has done.... ",n) start_time = time.time() t_objs = [] for i in range(50): t = threading.Thread(target = run,args = ("t-%s" %i,)) t.start() t_objs.append(t) for t in t_objs: t.join() print("------all threads has finished") print("cost : ",time.time()-start_time)
先自己创建一个临时列表,然后存储创建的线程,然后一个一个的用join()方法
创建了50个线程,主线程就是程序的本身,也就是说上面一共有51个线程
#threading.current_thread()显示当前线程是主线程还是子线程 #threading.active_count()显示当前程序运行的线程的个数 print("-----all threads has finished....",threading.current_thread(),threading.active_count())
守护进程
t.setDaemon(True) #把当前线程设置成守护线程,要在start()之前
非守护线程结束,所有守护线程就退出
1 import threading 2 import time 3 def run(n): 4 print("task ",n) 5 time.sleep(2) 6 print("task has done.... ",n) 7 start_time = time.time() 8 t_objs = [] 9 for i in range(50): 10 11 t = threading.Thread(target = run,args = ("t-%s" %i,)) 12 t.setDaemon(True)#把当前线程设置成守护线程,要在start()之前 13 t.start() 14 t_objs.append(t) 15 16 print("-----all threads has finished....",threading.current_thread(),threading.active_count()) 17 print("cost : ",time.time()-start_time)
运行结果:task has done都没有运行,主线程结束之后,所有子线程都强制结束
Semaphore(信号量)
互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据,如有3个座位,最多允许3个人同时在座位上,后面的人只能等到有人起来才能够坐进去
1 import threading,time 2 def run(n): 3 semaphore.acquire() 4 time.sleep(1) 5 print("run the thread:%s " %n) 6 semaphore.release() 7 8 if __name__ == '__main__': 9 10 semaphore = threading.BoundedSemaphore(5)#最多允许5个线程同时运行 11 for i in range(20): 12 t = threading.Thread(target = run,args=(i,)) 13 t.start() 14 15 16 while threading.active_count() != 1: 17 pass#print(threading.active_count()) 18 else: 19 print("----all threads done---") 20 21 #一次性执行了5个线程
和线程锁差不多,都要acquire()和release(),区别就是信号量有多把锁,
threading.BoundedSemaphore(5)最多允许5个线程同时运行,那运行结果就是5个结果一起出来,但是实质是这5个要是有3个先完成就会立刻再送进去3个,它不会等5个都完成,它是每出来一个就放进去一个
这5个如果同时改数据就有可能改错,这个主要用于:链接池 ,线程池。mysql链接有链接池的概念,同一时刻最多有几个并发;socketserver,为了保证系统不会被太多线程拉慢,可以用信号量弄同一时刻最多有100个链接进来
事件(event)
a events is a simple synchronization object;
the event represents an internal flag,and threads can wait for the flag to be set,or set or clear the flag themselves
事件是一个简单的同步对象,事件就相当于设置一个全部变量,然后线程不断的检测这个变量的变化然后进行不同的操作
方法:
event = threading.Event()生成一个event对象
event.set()
event.clear()
event.wait()
if the flag is set,the wait method doesn't do anything
标志位设定,代表绿灯,直接通行
if the flag is cleared,wait will block until it becomes set again
标志位被清空,代表红灯,wait等待变绿灯
any number of threads my wait for the same event
红绿灯案例:
1 import threading 2 import time 3 4 event = threading.Event() 5 def lighter(): 6 count = 0 7 event.set()#一开始先设成绿灯 8 while True: 9 if count > 5 and count <=10 :#改成红灯 10 event.clear()#清空标志位 11 print("