python3 多线程
进程——资源分配的最小单位,线程——程序执行的最小单位
什么是进程?
程序的执行实例称为进程。每个进程都提供执行程序所需的资源。一个进程有一个虚拟地址空间,可执行代码,打开系统对象的句柄,安全上下文,一个独特的过程,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("\033[31;1m红灯.STOP...\033[0m")
elif count >20 :
event.set() # 设置标志位
count = 0 #清零
else:
print("\033[36;1m绿灯.RUN...\033[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() #汽车的线程

浙公网安备 33010602011771号