线程与进程
1.线程:
os调用CPU进行运算的最小单位,被包含在进程中(就是一堆指令)
小知识点
运算速度比较:CPU>RAM>>磁盘
CPU 稍大于RAM(内存),RAM远大于磁盘
每一个程序的内存都是独立的,不能互相访问
单核CPU只能同时执行一个任务,但是因为太快了,在CPU内进行上下文切换(线程的上下文本质上是一组CPU的寄存器,有正在执行程序中的指针及堆栈指针。)
(1)以下是一个线程实例:
# coding:utf-8
# Author:Yang
import threading
import time
start_time=time.time()
# 多线程
def run(n):
print("task",n)
time.sleep(2)
print("task done",n)
t_objs=[] # 存线程实例
for i in range(50):
t =threading.Thread(target=run,args=("t%s"%i,))
t.start()
t_objs.append(t) # 为了不阻塞后面线程的启动,不在这里join,先放到一个列表里
for t in t_objs: # 想查看所有线程执行完的时间必须先创建个线程的列表 ,并for循环
t.join()
print(threading.current_thread(),threading.active_count()) # mainthread、 当前活跃线程
print("cost:",time.time()-start_time)
# 一个进程至少有一个线程,这段程序本身就是一个主线程,最后的cost时间是主线程执行的时间,所以说cost里没有2s,在for循环里只是开辟了子线程的执行
# 默认情况下主线程是不会等子线程执行完毕的
上述代码中,实例化线程写法为
t=treading.Thread(target=方法名,args=(参数,))
注意args中参数是一个元组,最后一个不写就是默认none t.start
一个进程至少有一个线程,这段程序运行时候本身起了一个主线程,最后的cost时间是主线程执行的时间,所以说cost里没有2s,在for循环里只是开辟了子线程的执行
默认情况下主线程是不会等子线程执行完毕的
那么如何去检测所有子线程执行结束的时间?
在python中使用t.join()是等待线程结束,在java,c#中是wait(),相当于不等待结果就不会往下走,变成串行的了
需要检测所有子线程执行结束时间,如下:
for i in range(50):
t =threading.Thread(target=run,args=("t%s"%i,))
t.start()
t_objs.append(t) # 为了不阻塞后面线程的启动,不在这里join,先放到一个列表里
for t in t_objs: # 想查看所有线程执行完的时间必须先创建个线程的列表 ,并for循环
t.join()
(2)除了过程写法 还有类式写法(用的较少):
#!/usr/bin/env python
# coding:utf-8
# Author:Yang
import threading
import time
# 类的写法来写多线程
# 这样写比较复杂
class MyThread(threading.Thread):
def __init__(self,n,sleep_time):
self.n=n
self.sleep_time=sleep_time
super(MyThread,self).__init__() # 因为继承父类 要重构父类的构造方法
def run(self): # 只能是run
print("running task",self.n)
time.sleep(self.sleep_time)
print("task done..",self.n)
t1=MyThread("t1",2)
t2=MyThread("t2",4)
t1.start()
# t1.join() # 其实就是有些语言中的wait() 等待线程结束 相当于不等待结果就不会往下走,变成串行的了
t2.start()
t1.join()
t2.join()
print("main thread...")
(3)多线程 守护线程
通过t.setDaemon(True)
把当前线程设置成守护线程(主线程结束都退出,不关心守护线程是否结束,例如写socket时,建立一个连接就是一个线程,当程序断开,所有socket都断开)
for i in range(50):
t =threading.Thread(target=run,args=("t%s"%i,))
t.setDaemon(True) # 把当前线程设置成守护线程(主线程结束都退出 不关心守护线程是否结束),就不用加join了
t.start()
t_objs.append(t) # 为了不阻塞后面线程的启动,不在这里join,先放到一个列表里
(4)互斥锁
需求,当一个线程在改全局变量的时候,防止另外的线程来改数据,需要加互斥锁。在python3.x不用加锁了,2.x在ubuntu上要加锁
#!/usr/bin/env python
# coding:utf-8
# Author:Yang
import threading
import time
# 互斥锁
start_time=time.time()
def run(n):
lock.acquire() # 获取锁
global num # 声明全局变量了
num+=1
lock.release()
lock=threading. Lock # 释放锁
# python 3.x不用加锁了 2.x在ubuntu上要加锁
num =0
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 finished---")
print("num:",num)
在 python2.x的ubuntu上会出现结果为(“num”,49)发现不是50了,说明有线程在改数据的时候,另外的线程也在操作数据
(5)递归锁
递归锁的使用场景:内部锁过多的时候
threading.Lock 内部锁过多会卡死,使用RLock递归锁 可以解除死锁
import threading, time
# 递归锁的使用场景 (内部锁过多的时候)
def run1():
print("grab the first part data")
lock.acquire()
global num
num += 1
lock.release()
return num
def run2():
print("grab the second part data")
lock.acquire()
global num2
num2 += 1
lock.release()
return num2
def run3():
lock.acquire()
res = run1()
print('--------between run1 and run2-----')
res2 = run2()
lock.release()
print(res, res2)
num, num2 = 0, 0
lock = threading.RLock() # threading.Lock 卡死 使用RLock 递归锁 可以解除死锁
for i in range(1):
t = threading.Thread(target=run3)
t.start()
while threading.active_count() != 1:
print(threading.active_count())
else:
print('----all threads done---')
print(num, num2)
(6)信号量
semaphore.acquire()
semaphore.release() semaphore =
threading.BoundedSemaphore(5) 最多允许五个线程同时运行,实际上是每出来一个就放进去一个
import threading, time
# 信号量
def run(n):
semaphore.acquire() # 信号量锁
time.sleep(1)
print("run the thread: %s\n" % n)
semaphore.release()
if __name__ == '__main__':
semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 实际上是每出来一个就放进去一个
for i in range(22):
t = threading.Thread(target=run, args=(i,))
t.start()
while threading.active_count() != 1:
pass # print threading.active_count()
else:
print('----all threads done---')
#print(num)
(7) 多线程典型模型 生产者消费者模型
目的 解耦 排队
import threading,time
import queue
# 生产者消费者模型 解耦 排队
q = queue.Queue(maxsize=10)
def Producer(name):
count = 1
while True:
q.put("骨头%s" % count)
print("生产了骨头",count)
count +=1
time.sleep(0.5)
def Consumer(name):
#while q.qsize()>0:
while True:
print("[%s] 取到[%s] 并且吃了它..." %(name, q.get()))
time.sleep(1)
p = threading.Thread(target=Producer,args=("Alex",))
c = threading.Thread(target=Consumer,args=("ChengRonghua",))
c1 = threading.Thread(target=Consumer,args=("王森",))
p.start()
c.start()
c1.start()
(8)event 事件 :
事件:线程可以关注另一个线程状态而做出的相应的响应
标志位:event.set() 先绿灯,默认True为绿灯
event.clear() 清空标志位 代表红灯
event.wait() 等待标志位
# 事件 线程可以关注另一个线程状态而做出相应的响应
import time
import threading
event=threading.Event()
def lighter(): # 路灯
count = 0
event.set() # 先绿灯 默认true为绿灯
# event.clear() 清空标志位 代表红灯
# event.wait() 等待标志位
while True:
if count >5 and count<10:
event.clear() # 把标志位清了
print("\033[41;1mred light is on...\033[0m")
elif count >10:
event.set() # 变绿灯
count =0
else:
print("\033[42;1mgreen light is on...\033[0m")
time.sleep(1)
count+=1
def car(name):
while True:
if event.is_set(): # 代表绿灯
print("[%s] running..."%name)
time.sleep(1)
else:
print("[%s] sees red light,waiting..."%name)
event.wait()
print("\033[34;1m[%s] green light is on ,start going...\033[0m"%name)
light = threading.Thread(target=lighter,)
light.start()
car1=threading.Thread(target=car,args=("Tesla",))
car1.start()
2.进程:
程序要以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等。。。所以线程可以定义为:
对一个程序各种资源管理的集合就叫做进程
进程要操作CPU,必须要先创建一个线程(至少一个)进程本身不具有执行功能,而是线程调用CPU
所有在同一个进程的线程共享同一块内存空间
3.进程与线程的区别:
(1)启动线程更快,但是在运行时进程与线程的速度一样
(2)线程共享内存空间,进程的内存是独立的
(3)线程与父进程共享内存数据,父进程数据改变后,线程的数据会改变。子进程是克隆父进程的数据,当父进程数据改变后,不会影响到子进程的数据
(4)同一个进程的线程之间可以直接交流。两个进程想通信,必须通过中间代理来实现
(5)创建新线程很简单。创建新进程需要对其父进程进行一次克隆
(6)一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作其子进程
(7)对一个线程进行需改会影响到同一进程的其他线程。对进程进行修改,但是子进程不会被修改
4.python的设计缺陷(python GIL —global interperter lock 全局解释器锁 )
注意,只是C python解释器的缺陷 像 JPython 等没有这个缺陷,像新的pypy解释器推出了无GIL的版本,用的最多的还是Cpython
缺陷:无论多少核CPU
永远都是在同一时间只有一个线程在运行(因为GIL),所以说python是假并行,因为python起的是通过cpython解释器下c语言调用系统的原生线程
5.队列(Queue)
作用:解耦,使程序直接实现松耦合,提高执行效率
class queue.Queue() # 先进先出
class queue.LifoQueue() # last in first out 后进先出
class queue.PriortyQueue() # 存储数据时可设置优先级队列
#!/usr/bin/env python
# coding:utf-8
# Author:Yang
import queue
# q=queue.Queue(maxsize=2)
# q=queue.LifoQueue()
# q.put("d1")
# q.put("d2")
#
# print(q.qsize())
# print(q.get())
# print(q.get())
q=queue.PriorityQueue()
q.put((-1,"a"))
q.put((10,"yang"))
q.put((3,"b"))
q.put((6,"c"))
print(q.get())
print(q.get())
print(q.get())
print(q.get())