python多线程第一讲:多线程入门

目录

python多线程第一讲:多线程入门
python多线程第二讲:Lock和RLock

什么是多线程

线程是程序执行流的最小单元,比线程更大的是进程。
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
进程在计算机中的存在形式通常是一个可见的软件,比如说QQ,而线程在QQ这个进程中的实际应用就是,QQ中会有多个线程来实现QQ的各类功能,比如在聊天的时候还能够收到邮件。
举例来说,有单核CPU的计算机,有三个线程A、B、C,分别代表聊天、收邮件和听歌功能他们的运行是并行的,并行即多个线程争夺cpu执行权,谁争夺到谁就执行。

什么是多线程编程

指的是线程间通过cpu调度实现线程交替运行的一种编程模式

多线程编程中的基本概念
并发、并行

并发就像一个人(CPU)喂两个小孩(程序)吃饭,表面上是两个小孩在吃饭,实际是一个人在喂。
并行就是两个人喂两个小孩子吃饭。
并发和并行区别如下图:
在这里插入图片描述##### 线程调度规律

CPU调度的基本单位是线程

在CPU比较繁忙资源不足的时候,操作系统只为一个含有多线程的进程分配仅有的CPU资源,这些线程就会自己争夺时间片。这就是多线程实现并发,线程之间争夺CPU资源获得执行机会。

在CPU资源较足的时候,一个进程内的多线程,可以被分配到不同的CPU资源,这就是多线程实现并行。

python中的GIL

GIL的意思是全局解释性锁,python中的线程对应C语言中的一个线程,GIL使得同一时刻只能有一个python程序运行在cpu上,一个python程序由1或多个线程组成,所以Python的线程只能并发不能并行。

线程基本使用
total = 0

def add():
    global total
    for i in range(1000000):
        total += 1
def desc():
    global total
    for i in range(1000000):
        total -= 1

import threading
# 第一个参数target传入一个函数,指示将哪个函数抛到线程中运行
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
# 开启一个线程
thread1.start()
thread2.start()

# 在主线程调用 子线程.join() 函数的作用是:阻塞主线程直到子线程执行完成
thread1.join()
thread2.join()
print(total)

上面的代码都运行完了之后打印total但是结果不是0。这是因为线程安全问题导致的。如果像下面这样调用就不会有问题:

total = 0

def add():
    global total
    for i in range(1000000):
        total += 1
def desc():
    global total
    for i in range(1000000):
        total -= 1

add()
desc()
print(total)
线程安全

线程安全指的是一段代码,在多线程或者单线程情况下执行的结果都是一致的。

线程安全函数:

符合线程安全定义的函数称为线程安全函数

线程安全类:

一个类的所有对外开放的函数都是线程安全函数,则说这个类是线程安全类

线程安全问题怎么产生的

这里回到我们上文的那段代码

total = 0

def add():
    global total
    for i in range(1000000):
        total += 1
        
def desc():
    global total
    for i in range(1000000):
        total -= 1

import threading
# 第一个参数target指示将哪个函数抛到线程中运行
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
# 开启一个线程
thread1.start()
thread2.start()

# 在主线程调用 子线程.join() 函数的作用是 阻塞主线程直到子线程执行完成
thread1.join()
thread2.join()
print(total)

这个函数执行结果在单线程下是0,多线程下不是0。这就说明这两个函数不是线程安全的,那是什么导致的呢——共享变量。

两个函数都涉及到对共享total的操作,分别执行了 total += 1total -= 1以第一个举例:

编译器在运行total += 1的时候其实不是做了一件事情,而是做了四件事情

1、LOAD total原来的值

2、LOAD 常量1的值

3、计算 total+1的结果

4、将 total+1的结果赋值给total

编译器在运行total -= 1的时候其实不是做了一件事情,而是做了四件事情

5、加载total原来的值

6、加载常量1的值

7、计算total-1的结果

8、将total+1的结果赋值给total

多线程情况下

1234 5678这种没问题

1 5678 234就会有问题

之所以出现这种问题是因为信息不对称。解决线程安全问题就是要使信息变得对称。这就要用到线程同步。
这个我下篇文章讲。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值