python 多线程详细讲解

这几天又重新学习了一遍多线程和多进程的知识点。为了方便以后查看,将自己的理解写在博客里。

学习多线程和多进程我们需要先理解什么是多线程,什么是多进程。还需要理解同步,异步,并发,并行等概念

我始终学习一种方法必须将方法的本质先记住,理解概率是为了我们改好的学习,

并发:系统具有处理多个任务的能力

并行:系统具有  同时 处理多个任务的能力

多线程:是指在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
这三行代码,
我们的其它代码是可以运行的,因此这样看来是有效的,(本例子只使用了三行代码,不代表只有三行代码)

这样我们就解决了这个问题,先讲到这里,下一篇将会详细介绍多线程里面几种不同的锁,如果文中有错误的地方,希望大家留言,我会尽快改正,感谢大家的批评指导,一起进步。

猜你喜欢

转载自blog.csdn.net/weixin_42236288/article/details/94322436