一、什么是多线程
线程
A thread is an execution context,which is all the information a CPU needs to execute a stream of instructions.
那么到底什么是多线程呢?说到多线程那么你一定会想到多进程,我这里打一个比方,加深大家的理解。 进程可以比作一个空旷的教室,线程可以比作里面上课的同学。就是说一个进程可以包含多个线程,每一个同学都在教室干自己的事情,看书、玩手机、听课…就是说线程就是进程中的一个单一顺序的控制流,一个进程可以并发多个线程,每条线程执行不同的指令。
这样你对进程和线程是不是有一个大概的理解了。
注意事项
- 进程要操作CPU,必须要先创建一个线程,所有在同一个进程里的线程是共享同一块内存空间的。
- 进程是独立的,父进程的两个子进程独立,不能互相访问,想通信要通过中间代理。
- 进程创建的两个线程共享一个数据。 一个线程可以控制同一个进程里的线程。但是进程只能操作子进程。
下面我们启动一个线程:
import threading #导入线程模块
import time
def run(n): #定义一个函数用来测试线程
print('task',n)
time.sleep(2)
t1=threading.Thread(target=run,args= ('t1',)) #生成线程t1
t2=threading.Thread(target=run,args= ('t2',))
t1.start() #启动线程t1
t2.start()
我们看一下结果:
发现线程成功的调用了run函数。那么你不禁有一个疑惑,为什么不直接调用函数去执行呢?
run('t1')
run('t2')
也会达到同样的效果,但是多线程最大的优点就是可以把任务分块执行,分块后可以同时进行而不用等待,这样效率更高。我们来通过一个简单的代码来举一个例子,我们生成50个线程,每一个线程都来调用一个函数,看一看线程执行的时间。
import threading ,time
#启动50个线程
start_time=time.time() #记录当前时间
t_objs=[]
def run1(n):
print('task',n,threading.current_thread()) #打印 task+tn+当前线程
time.sleep(2) #延时2秒
for i in range(1,51):
t1 = threading.Thread(target=run1, args=('t%s'%i,)) #给定参数t1,t2...tn
t1.start()
t_objs.append(t1)
for r in t_objs:
r.join() #等待子线程执行完毕再继续执行
print('我是主线程',threading.current_thread() ) #打印主线程
print('cost time:',time.time()-start_time ) #这样才能打印所有线程执行完的时间
我们看一下结果:
启动了50个线程去完成50次函数的调用仅仅花了调用一次函数的时间,可以说多线程执行代码的效率很高。这里说一个很重要的事情,刚才生成了50个线程,但是实际上有51个线程(1个主线程,50个子线程)
二、什么是线程锁
线程锁:顾名思义就是用锁锁住该线程要更改的数据,防止该线程未执行完之前,其它线程修改该线程的数据。一旦用线程锁之后就会防止数据错乱。简单演示一下线程锁:
def run(n): #在子线程中修改主线程的全局变量
lock.acquire() #获取一把锁 把数据锁住防止其他线程修改 锁住之后程序编成串行 执行时间变长
global num
time.sleep(0.1)
num+=1
lock.release() #释放锁
lock=threading.Lock() #线程锁实例
num=0 #在主线程的全局变量
t_objs=[]
for i in range(1,101): #生成100个线程
t1 = threading.Thread(target=run, args=('t%s'%i,))
t1.start()
t_objs.append(t1)
for r in t_objs:
r.join()
print('我被修改了%s次'%(num)) #100个线程共同修该num
递归锁(互斥锁):就是两层线程锁,举一个例子我们有两把钥匙分别开大门和里面的小门,当进去以后想出来,就要先开小门再开大门,但是一旦程序分不清楚钥匙,就会 拿大门钥匙开小门,程序就会卡死。解决办法可以声明一个字典,存储门和钥匙信息。
#递归锁
import threading ,time
def run1(): #小门1
print('grab the first part data')
lock.acquire()
global num1
num1+=1
lock.release()
return num1
def run2(): #小门2
print('grab the first 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 )
num1,num2=0,0
lock=threading.Lock()
for i in range(10): #11个线程
t=threading.Thread(target=run3)
t.start()
while threading.active_count() != 1: #只剩一个线程 就代表所有线程执行完毕(1个主线程10个子线程)
print(threading.active_count())
else:
print('----all threads done----')
print(num1,num2)
执行后我们发现程序卡死一直打印11。
解决办法:在声明锁时候lock=threading.Lock() 改成递归锁:lock=threading.RLock() 这次程序顺利执行。