这几天又重新学习了一遍多线程和多进程的知识点。为了方便以后查看,将自己的理解写在博客里。
学习多线程和多进程我们需要先理解什么是多线程,什么是多进程。还需要理解同步,异步,并发,并行等概念
我始终学习一种方法必须将方法的本质先记住,理解概率是为了我们改好的学习,
并发:系统具有处理多个任务的能力
并行:系统具有 同时 处理多个任务的能力
多线程:是指在cpu上运行的n个不断切换着占用cpu的程序,这些程序是并发进行的(线程是最小的运行单位,同时线程的资源是共享的,共享一块内存)
多进程:是指在多快cpu上同时运行程序,这些程序是并行进行的(进程是最小的资源单位,使用不同的内存区域)
同步:当进程执行到一个I/o操作时, 需要等待数据传输完毕在继续执行
异步:当进程执行到一个I/o操作时,不需要等待数据传输完毕,而是执行其他程序,直到数据传输完毕再回来继续运行
了解了理论知识,接下来我们使用代码进行实现,在python中,对于多线程提供了一个模块,threading,下面来看一下threading的使用:
首先我们梳理一下线程的一个建立到运行的过程,创建一个程序——》创建线程——》启动线程——》线程运行结束。
import threading #导入threading库
import time
def he():
print('1_hello')
time.sleep(4)
print('1_heend')
def wd(ss):
print(ss)
time.sleep(4)
print('2_wdend')
t1 = threading.Thread(target=he) #target需要开启线程的函数
t2 = threading.Thread(target=wd,args=('2_world',)) #args=(需要传入的参数,参数后面一定要跟上逗号,否则会报错)
t1.start()
t2.start()
我们看一下运行结果,
我们分析一下结果,同时开启t1,t2,第一个print执行,四秒后,第二个print执行,运行结束
这样我们不容易看出线程的切换效果,我们更改一下,wd,的time.sleep(2),此时我们看见的执行效果如下
在这里我们看到,时间短的wd()运行后由于sleep时间短,先打印了第二次,
现在我们已经学会了如何创建线程,开启进程,下面我们继续进步
在代码后面添加这几行代码
threads=[]
threads.append(t1)
threads.append(t2)
for t in threads:
t.start()
for t in threads:
t.join()
这样可以调用线程,为什么要使用这样的形式呢,我们接着看,我们看到我们上面加了jion这这个方法,jion,作用是,等当前启动的线程运行结束才进行下一个线程。其实使用for 循环启动在这个地方也有一个好处,当需要启动的线程较多的时候我们不用一个个写jion,当然jion运用中也并不是都需要使用,而是根据具体的情况来定。
为了能够更好的理解线程的切换过程,我们再用一个例子来具体感受一下。
我们不妨先猜一下下面两段代码运行结果,记住你猜的数,继续看,
'''
下面代码实现100线程对100进行减少到0
'''
def sub1():
global num
time.sleep(0.1)
num -=1
time.sleep(0.1)
num=100
l=[]
for i in range(100):
t = threading.Thread(target=sub1)
t.start()
l.append(t)
for t in l:
t.join()
print(num)
#我们实现一下开启100个线程来对num进行累减
def sub1():
global num
tem =num
time.sleep(0.01) #分别更改时间为1,0.1,0.01,0.001,看看输出的结果
num =tem-1
num=100
l=[]
for i in range(100):
t = threading.Thread(target=sub1)
t.start()
l.append(t)
for t in l:
t.join() #使用join,等待100个线程结束后,主线程才继续运行
print(num)
先别往下,先看一下代码,然后将得出你的答案
第一段代码结果为0
大家猜对了吗
第二段代码不同时间得到的数
99,99,97,87,这是在我的电脑上的出的结果,
为什么看似差不多的两段代码得出的结果相差这么大了,这是因为第一段代码,每一个线程所得到的数字都是前面一个减一后的数,既是启动下一个线程的时间,大于上一个线程的执行时间,而第二段代码就是小于的情况,
线程间造成了数据安全问题,数据被线程相互进行了修改
A1-A100处理时间小于循环时间则不会发生这种情况,大于时会发生,
当运行到睡眠时,其他线程应该运行,此时取的num会等于100,而不是99
因此如果处理时间大于循环时间,则所有返回的都是100-1
处理时间小于循环时间,部分线程取到的数据已经减少,(这部分线程分布于A1-A100之间)因此会num会继续减小
这就是为什么数值会不断变化的原因
我们怎么解决这样的问题呢,现在我们需要引入一个新的概念,锁,我们直接看代码
def sub1():
global num
lock.acquire() #加锁
tem =num
time.sleep(0.01)
num =tem-1
lock.release() #解锁
num=100
l=[]
lock = threading.Lock() #创建锁
for i in range(100):
t = threading.Thread(target=sub1)
t.start()
l.append(t)
for t in l:
t.join()
print(num)
加锁的作用是当运行到这里时,由于加了锁其他线程将不会抢占cpu,直到锁被解除。
整个过程看上去像是重新回到了串行的过程,看似多线程已经没有作用
但是加锁我们只针对
tem =num
time.sleep(0.01)
num =tem-1
这三行代码,
我们的其它代码是可以运行的,因此这样看来是有效的,(本例子只使用了三行代码,不代表只有三行代码)
这样我们就解决了这个问题,先讲到这里,下一篇将会详细介绍多线程里面几种不同的锁,如果文中有错误的地方,希望大家留言,我会尽快改正,感谢大家的批评指导,一起进步。