最近看了一些多线程的东西,慢慢整理如下
一、多线程
首先什么是多线程?通俗点讲,我们常见的看视屏啊、word写文档,内部都是多线程的模式执行的,因为看视屏需要有图像和声音,你不可能先看完图像再听声音,反之也不可以;word写文档时,你既要写文档,又要实时检查错误和保存,也是一个多线程的过程。把分任务分成每一个的线程,然后依次分时间的执行,比如A任务执行0.01秒,B任务执行0.02秒,由于CPU的调度执行速度太快了,我们基本看不出先后,所以我们感觉这个任务就跟同时执行一样。
二、python语法
Python的标准库提供了两个模块:_thread
和threading
,_thread
是低级模块,threading
是高级模块,对_thread
进行了封装。绝大多数情况下,我们只需要使用threading
这个高级模块。
1、启动一个线程就是把一个函数传入并创建Thread
实例,然后调用start()
开始执行。我们创建一个线程执行的函数,然后启动两个线程执行:
# -*- coding: utf-8 -*-
"""
使用threading实现多线程
"""
import time, threading
# 新线程执行的代码
def loop(max):
print("thread %s is running..." % (threading.current_thread().name))
n = 0
while n < max:
n += 1
print("thread %s >>> %s" % (threading.current_thread().name, n))
time.sleep(1)
print("thread %s ended" % (threading.current_thread().name))
def threading_1():
# 进程进来默认启动一个主线程
print("thread %s is running..." % (threading.current_thread().name))
# 启动两个线程执行,观察基本同时执行的过程
t_example1 = threading.Thread(target=loop, args=(5,), name="loopThread1")
t_example2 = threading.Thread(target=loop, args=(10,), name="loopThread2")
t_example1.start()
t_example2.start()
t_example1.join()
t_example2.join()
print("thread %s ended" % (threading.current_thread().name))
if __name__ == '__main__':
threading_1()
两个线程执行的结果
thread MainThread is running...
thread loopThread1 is running...
thread loopThread1 >>> 1
thread loopThread2 is running...
thread loopThread2 >>> 1
thread loopThread2 >>> 2
thread loopThread1 >>> 2
thread loopThread1 >>> 3
thread loopThread2 >>> 3
thread loopThread2 >>> 4
thread loopThread1 >>> 4
thread loopThread2 >>> 5
thread loopThread1 >>> 5
thread loopThread2 >>> 6
thread loopThread1 ended
thread loopThread2 >>> 7
thread loopThread2 >>> 8
thread loopThread2 >>> 9
thread loopThread2 >>> 10
thread loopThread2 ended
thread MainThread ended
2、比较两个线程执行和两个进程执行的时间
# 比较两个进程和两个线程执行的时间
def compare_time():
start1 = time.time()
threading_1()
print("threading time:",time.time()-start1)
start2 = time.time()
loop(5)
loop(10)
print("processing time:", time.time()-start2)
if __name__ == '__main__':
#threading_1()
compare_time()
上述程序执行结果:
thread MainThread is running...
thread loopThread1 is running...
thread loopThread1 >>> 1
thread loopThread2 is running...
thread loopThread2 >>> 1
thread loopThread2 >>> 2
thread loopThread1 >>> 2
thread loopThread1 >>> 3
thread loopThread2 >>> 3
thread loopThread2 >>> 4
thread loopThread1 >>> 4
thread loopThread2 >>> 5
thread loopThread1 >>> 5
thread loopThread2 >>> 6
thread loopThread1 ended
thread loopThread2 >>> 7
thread loopThread2 >>> 8
thread loopThread2 >>> 9
thread loopThread2 >>> 10
thread loopThread2 ended
thread MainThread ended
threading time: 10.00826644897461
thread MainThread is running...
thread MainThread >>> 1
thread MainThread >>> 2
thread MainThread >>> 3
thread MainThread >>> 4
thread MainThread >>> 5
thread MainThread ended
thread MainThread is running...
thread MainThread >>> 1
thread MainThread >>> 2
thread MainThread >>> 3
thread MainThread >>> 4
thread MainThread >>> 5
thread MainThread >>> 6
thread MainThread >>> 7
thread MainThread >>> 8
thread MainThread >>> 9
thread MainThread >>> 10
thread MainThread ended
processing time: 15.011106252670288
执行结果可以看出:多线程执行的过程基本是同时的,且比顺序执行这几个进程执行速度快。
3、加锁机制:当多个线程同时修改一个数据时,容易造成把数据改乱。所以需要把每个线程加锁,使该线程执行时,不被其他线程干扰。
我们定义了一个共享变量balance
,初始值为0
,并且启动两个线程,先存后取,理论上结果应该为0
,但是,由于线程的调度是由操作系统决定的,当t1、t2交替执行时,只要循环次数足够多,balance
的结果就不一定是0
了。
# -*- coding: utf-8 -*-
"""
【多线程加锁机制】
多个线程同时改一个变量,容易把内容给改乱了
"""
import time, threading
# 假定这是银行存款
balance = 0
def change_it(n):
global balance
# 先存
balance += n
# 再取
balance -= n
# 线程运行
def run_thread(n):
for i in range(1000000):
change_it(n)
# 开启两个线程执行
def threading_1():
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
if __name__ == '__main__':
threading_1()
执行结果:16(不同执行情况不同)
执行时,只要循环次数足够多,balance
的结果就不一定是0
了。
原因是因为高级语言的一条语句在CPU执行时是若干条语句,即使一个简单的计算:
balance = balance + n
也分两步:
- 计算
balance + n
,存入临时变量中; - 将临时变量的值赋给
balance
。
也就是可以看成:
x = balance + n
balance = x
究其原因,是因为修改balance
需要多条语句,而执行这几条语句时,线程可能中断,从而导致多个线程把同一个对象的内容改乱了。
两个线程同时一存一取,就可能导致余额不对,你肯定不希望你的银行存款莫名其妙地变成了负数,所以,我们必须确保一个线程在修改balance
的时候,别的线程一定不能改。
如果我们要确保balance
计算正确,就要给change_it()
上一把锁,当某个线程开始执行change_it()
时,我们说,该线程因为获得了锁,因此其他线程不能同时执行change_it()
,只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()
来实现:
# -*- coding: utf-8 -*-
"""
【多线程加锁机制】
多个线程同时改一个变量,容易把内容给改乱了
"""
import time, threading
# 假定这是银行存款
balance = 0
def change_it(n):
global balance
# 先存
balance += n
# 再取
balance -= n
# 加锁线程执行
lock = threading.Lock()
def run_thread_lock(n):
for i in range(1000000):
# 获取锁
lock.acquire()
try:
# 放心修改
change_it(n)
finally:
# 每次修改完都要释放锁,这样确保可以释放锁
lock.release()
# 开启两个线程执行,加锁
def threading_2():
t1 = threading.Thread(target=run_thread_lock, args=(5,))
t2 = threading.Thread(target=run_thread_lock, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
if __name__ == '__main__':
threading_2()
执行结果永远为0