IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python----线程(Threading)---线程安全问题 & 互斥锁 & 队列 -> 正文阅读

[Python知识库]Python----线程(Threading)---线程安全问题 & 互斥锁 & 队列

1.线程安全的问题?

因为线程之间存在资源竞争的情况,也就是说一个全局变量经过多个线程的共同操作,最终的结果出现异常情况,这就是线程安全的问题

num = 0


def sum_one(quantity):
    global num
    for index in range(quantity):
        num += 1
    return num


t1 = Thread(target=sum_one, args=(1000000,))
t2 = Thread(target=sum_one, args=(2000000,))
t3 = Thread(target=sum_one, args=(3000000,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print("num-------->", num)


# 结果
num--------> 4565212

以上频率,如果降低到万级别或更低频率其实结果是正确的。由于存在这样的偶然性,所以认为存在线程间安全的问题。那产生的原因是什么呢?如何解决呢?



2.线程安全产生的根源?

为了保证使用共享数据的并发编程能正确、高效协作,对于一个好的解决方案,需要满足以下四个条件:

  • 任何俩个线程不能同时处于临界区
  • 不应对cpu的速度和数量做任何假设
  • 临界区外运行的线程不能阻塞其他线程
  • 不得使线程无限期等待进入临界区

在这里插入图片描述

以上内容大白话是:

  • 1.当某个线程执行一个函数的时候(赋值或其他操作),可以理解为执行状态(临界区)。这执行状态如果分为多步骤,中途需要阻塞其他线程的操作,直到这个步骤结束。
  • 2.如果执行的过程是“原子性”的,只有一步就操作完,则不会出现赋值错乱情况。


【错误过程的解读?】

在这里插入图片描述

(1)自身赋值的过程分为2步,第一步拿到g_num的值并加1, 第二部把相加之后的值赋值为g_num。

(2)因线程之间的资源的竞争问题,执行的顺序是随机的。如果t1先拿到原始值0,加完之后为1,但此时t2也拿到原始值0,加完之后也为1。

(3)t1执行第二步骤,存储g_num的值为1。由于t2和t1之间没有通讯,t2认为g_num的值还是最初的值,就把t2计算的结果1,再次赋值为g_num。

(4)最终对g_num赋值了2次,都是1,结果导致最终计算的值偏小。



3.原子操作?

原子操作(atomic operation),指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会切换到其他线程。它有点类似数据库中的 事务。


【Python3中常见的原子操作?】

L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()


【Python3中非原子操作?】

i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1



4.线程间通讯----互斥锁?

  • 通过线程锁,可以解决线程安全的问题。原理就是,把不是原子操作的步骤,模拟出来原子操作(在某线程多步骤执行的时候,其他线程都阻塞)。
  • 通过线程锁, 就可以实现了线程之间的通讯

【案例一】:多个线程,每个线程执行的次数通过参数传递


num = 0

mutex = threading.Lock()

# 方式一:
def sum_one(quantity):
    global num
    for index in range(quantity):
        with mutex:		# mutex自动控制锁的释放
            num += 1
    return num


# 方式二:
def sum_one(quantity):
    global num
    for index in range(quantity):
        mutex.acquire()		# 上锁
        num += 1
        mutex.release()		# 释放锁
    return num


t1 = Thread(target=sum_one, args=(1000000,))
t2 = Thread(target=sum_one, args=(2000000,))
t3 = Thread(target=sum_one, args=(3000000,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print("num-------->", num)



# 结果
num--------> 6000000


【案例二】:多个线程,所有线程执行的次数固定

number = 0
quantity = 500000

'''创建一个互斥锁,默认是没有上锁的'''
mutex = threading.Lock()

# 方式一:
def sum_func(param):
    global number, quantity
    while True:
        '''上锁'''
        mutex.acquire()
        if quantity > 0:
            print("num & quantity------->", number, quantity)
            number += 1
            quantity -= 1
            print("[%s] number=%d" % (param, number))
            mutex.release()
        else:
            print("===========================>>>>")
            mutex.release()
            break


# 方式二:
def sum_func(param):
    global number, quantity
    while True:
        '''上锁'''
        with mutex:
            if quantity > 0:
                number += 1
                quantity -= 1
            else:
                print("===========================>>>>")
                break


def main():
    t1 = threading.Thread(target=sum_func, args=("t_1",))  # 加上要传递的参数,元组类型
    t2 = threading.Thread(target=sum_func, args=("t_2",))
    t3 = threading.Thread(target=sum_func, args=("t_3",))
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print("number-------->", number)


if __name__ == '__main__':
    main()


# 结果(打印相对很费时间,过程数据如下)

num & quantity-------> 499996 4
[t_3] number=499997
num & quantity-------> 499997 3
[t_2] number=499998
num & quantity-------> 499998 2
[t_2] number=499999
num & quantity-------> 499999 1
[t_2] number=500000
===========================>>>>
===========================>>>>
===========================>>>>
number--------> 500000



5.线程间通讯----队列?

  • 线程间通讯,还会使用到队列,因为队列的写入读取。注意啦,只有队列的写入、读取是原子的。
  • 如果读完信息进行赋值操作,这个赋值步骤还是需要添加锁的!!!

from queue import Queue

shopping_cart = Queue(maxsize=10)


def produce_product(quantity):
    for index in range(quantity):
        product_name = "product_" + str(index)
        shopping_cart.put(product_name)
        print("produce_product--------->", product_name, "\n")
        time.sleep(0.2)


def consume_product():
    while True:
        try:
            product_name = shopping_cart.get(timeout=1)
            time.sleep(0.1)
            print("consume_product----------->", product_name, "\n")
        except Exception as e:
            print("consume_product----------->finish!!!", e, "\n")
            break


def main():
    t1 = threading.Thread(target=produce_product, args=(10,))  # 加上要传递的参数,元组类型
    t2 = threading.Thread(target=consume_product)
    t1.start()
    t2.start()
    t1.join()
    t2.join()


if __name__ == '__main__':
    main()

# 结果
produce_product---------> product_0 
consume_product-----------> product_0 
produce_product---------> product_1 
consume_product-----------> product_1 
......
produce_product---------> product_9 
consume_product-----------> product_9 
consume_product----------->finish!!!  




参考博文:《通俗易懂:说说 Python 里的线程安全、原子操作 》

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-02-24 15:14:36  更:2022-02-24 15:17:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 0:23:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码