进程——资源分配的最小单位,线程——程序执行的最小单位
什么是进程?
程序的执行实例称为进程。每个进程都提供执行程序所需的资源。一个进程有一个虚拟地址空间,可执行代码,打开系统对象的句柄,安全上下文,一个独特的过程,pid标识符,环境变量,优先级类,最小和最大工作集大小,并且至少有一个执行线程。每个进程都是从一个线程开始的,通常被称为主主线程,但是可以创建额外的任何线程的线程。
进程与线程的区别?
线程共享内存空间,进程的内存是独立的,同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现,创建新线程很简单, 创建新进程需要对其父进程进行一次克隆,一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程
线程基本函数,
run() 里面是需要执行的命令,
start() 线程启动函数,
join() 等待该线程结束,
setDaemon(True) 设置为守护线程,设置守护线程需要放在线程启动前
Lock() 线程锁,防止数据的不准确行,线程执行时添加互斥锁
RLock() 递归锁,多个线程时需要添加递归锁,否则会出现锁死
BoundedSemaphore() 信息量,允许同时执行的最大线程数
threading.current_thread() ,当前线程;
threading.active_count() 目前活跃的线程数
1.简单线程的写法:
import threading from time import sleep '''多线程 第一种写法''' def run(t): print("task-%s" %t) sleep(2) print("task-%s is done" %t) t1 = threading.Thread(target=run,args=("t1",)) #参数后面需要带逗号,不然会误解析为一个参数 t2 = threading.Thread(target=run,args=("t2",)) t1.start() t2.start() '''多线程 第二种写法''' class MyThread(threading.Thread): def __init__(self,t): super(MyThread, self).__init__() #继承父类的init函数 self.t = t def run(self): print("task-%s"%self.t) sleep(2) print("task-%s is done"%self.t) if __name__ == "__main__": t1 = MyThread("t1") t2 = MyThread("t2") t1.start() t2.start() '''多线程 第三种写法''' def run(t): print("task-%s" % t) sleep(2) print("task-%s is done" % t) for i in range(2): t = threading.Thread(target=run, args=("t1",)) # 参数后面需要带逗号,不然会误解析为一个参数 t.start()
这三种的运行结果都一样:
task-t1 task-t1 task-t1 is done task-t1 is done Process finished with exit code 0
2.计算所有线程运行的时间
import threading import time '''多计算所有线程运行的时间,这里需要给每个线程添加join''' def run(t): print("task-%s" % t) time.sleep(2) print("task-%s is done" % t) st_time = time.time() #开始时间 tups = [] #所有的线程对象列表 for i in range(2): t = threading.Thread(target=run, args=("t1",)) # 参数后面需要带逗号,不然会误解析为一个参数 t.start() tups.append(t) #所有线程启动后,添加join,等待结束后,主线程再继续 for i in tups: i.join() print(time.time()-st_time) #需要时间
运行结果:
task-t1 task-t1 task-t1 is done task-t1 is done 2.0008037090301514 Process finished with exit code 0
3.设置守护线程
import threading import time '''程序本身就是主线程,守护线程是伴随主线程存在的,主线程关闭是不会等待守护线程,同时守护线程也会关闭''' def run(t): print("task-%s" % t) time.sleep(2) print("守护线程:", threading.current_thread()) print("task-%s is done" % t) st_time = time.time() #开始时间 tups = [] #所有的线程对象列表 for i in range(10): t = threading.Thread(target=run, args=(i,)) # 参数后面需要带逗号,不然会误解析为一个参数 t.setDaemon(True) #设置为守护线程 t.start() print("主线程:",threading.current_thread()) print("运行时间",time.time()-st_time) #需要时间
运行结果:
task-0 task-1 task-2 task-3 task-4 task-5 task-6 task-7 task-8 task-9主线程: <_MainThread(MainThread, started 6800)> 运行时间 0.0 Process finished with exit code 0
3.子线程的相互交互
同一个进程里面的线程共享同一块空间,可以共享内存中的资源
由于python的多线程基于GIL(全局解释器锁)原理,是通过单核上下文切换实现的多线程 ,所以在多个子线程同时修改一个数据时,会存在这个还没改完就切换的情况,当时数据不准确,因此我们需要给每个子线程的操作加上线程锁(互斥锁),每次执行先获取锁,执行完释放锁;但是加上锁后其实就不是多线程执行了,而是变成串行(一个一个的执行),在所中间加上sleep()就可以看出效果
import threading,time num = 0 lock = threading.Lock() def run(): lock.acquire() #获取锁 global num #声明全局变量 time.sleep(1) num += 1 print(num,time.time()) lock.release() #释放锁 for i in range(10): t = threading.Thread(target=run) t.start() print(num)
运行结果:
0 1 1542440306.993258 2 1542440307.99377 3 1542440308.9952087 4 1542440309.9967873 5 1542440310.9977813 6 1542440311.9988816 7 1542440312.9992244 8 1542440314.000361 9 1542440315.001824 10 1542440316.002374 Process finished with exit code 0
4.递归锁
threading.RLock()为递归锁,当添加多个锁的时候就应该用递归锁,不然程序会锁死,成为死循环
import threading def run1(): print("grab the first part data") rlock.acquire() #第二次加锁 内层锁 global num num += 1 rlock.release() #释放内层锁 return num def run2(): print("grab the second part data") rlock.acquire() global num2 num2 += 1 rlock.release() return num2 def run3(): rlock.acquire() #第一次加锁, 最外层锁 res = run1() print('--------between run1 and run2-----') res2 = run2() rlock.release() #释放最外层锁 print(res, res2) if __name__ == '__main__': num, num2 = 0, 0 rlock = threading.RLock() for i in range(5): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: #判断子线程是否执行完 print(threading.active_count()) #递归锁 else: print('----all threads done---') print(num, num2)
运行结果:
grab the first part data --------between run1 and run2----- grab the second part data 1 1 grab the first part data --------between run1 and run2----- grab the second part data 2 2 grab the first part data2 2 2 --------between run1 and run2----- grab the second part data 3 3 ----all threads done--- 3 3 Process finished with exit code 0
5.信息量
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去,从运行结果来看5个线程同时运行的。
import threading,time num = 0 semaphore = threading.BoundedSemaphore(5) #信息量, 同时最多允许5个线程 def run(): semaphore.acquire() #获取信息量 global num #声明全局变量 time.sleep(1) num += 1 print(num,time.time()) semaphore.release() #释放信息量 for i in range(20): t = threading.Thread(target=run) t.start() print(num)
运行结果:
0 1 1542440702.8448975 2 1542440702.845902 3 1542440702.845902 4 1542440702.845902 5 1542440702.8469012 6 1542440703.8459327 7 1542440703.846936 8 1542440703.846936 9 1542440703.847937 10 1542440703.847937 11 1542440704.847123 12 1542440704.848123 13 1542440704.848123 14 1542440704.8491254 15 1542440704.8491254 16 1542440705.8484023 17 1542440705.8494039 18 1542440705.8494039 19 1542440705.8504024 20 1542440705.8504024 Process finished with exit code 0
6.python自带的队列 Queue() LifoQueue() PriorityQueue()
import queue ''' python自带的队列 ''' q = queue.Queue() #先入先出 lq = queue.LifoQueue() #后进先出 (用法同Queue()) q.put(1,block=True,timeout=1) #block=True 没有数据会锁死,False不会锁死, timeout 超时时间 q.put(2) q.put(3) print("Queue()大小:", q.qsize()) # 队列中的大小 for i in range(3): q1 = q.get(block=True,timeout=2) #block=True 没有数据会锁死,False不会锁死, timeout 超时时间 print(q1) pq = queue.PriorityQueue() #根据优先级进出 pq.put(5,'jiad') pq.put(-2,'ieamd') pq.put(9,'bmoad') print("PriorityQueue()大小:", pq.qsize()) # 队列中的大小 for i in range(3): pq1 = pq.get(block=True,timeout=2) #block=True 没有数据会锁死,False不会锁死, timeout 超时时间 print(pq1)
运行结果:
Queue()大小: 3 1 2 3 PriorityQueue()大小: 3 -2 5 9
通过队列实现生产者消费者关系
import threading,time,queue q = queue.Queue(maxsize=10) def Producer(name): ''' 生产者 ''' count = 1 while True: q.put("酱骨头%s"%count) print("已经生产的数量:",count) time.sleep(2) count +=1 def Consumer(name): while True: while q.qsize()>0: print("%s取到了%s,并且吃了它"%(name,q.get())) time.sleep(1) p = threading.Thread(target=Producer,args=("张三",)) c1 = threading.Thread(target=Consumer,args=("李四",)) c2 = threading.Thread(target=Consumer,args=("王五",)) p.start() c1.start() c2.start()
7.event 事件,可以设置标志位(event.set()),清空标识位(event.clear()),等待(event.wait()) 是否存在标识位event.is_set()
import threading,time event = threading.Event() def lighter(): ''' 模拟红绿灯 10秒钟绿灯,10秒钟红灯 有标识位表示绿灯,没有表示红灯''' count = 0 # 时间长度 event.set() # 设置标识位 while True: if count>10 and count<=20: #红灯 event.clear() # 把标志位清了 print(" 33[31;1m红灯.STOP... 33[0m") elif count >20 : event.set() # 设置标志位 count = 0 #清零 else: print(" 33[36;1m绿灯.RUN... 33[0m") time.sleep(1) count += 1 def car(name): ''' 汽车的线程 ''' while True: if event.is_set(): #存在标识位 print("%s is run..."%name) time.sleep(1) else: print("红灯需要等待...") event.wait() lt = threading.Thread(target=lighter,) lt.start() #红绿灯的线程 ct = threading.Thread(target=car,args=("Tesla",)) ct.start() #汽车的线程