python中解决多线程同时调用同一函数导致错误的简单方法

        午睡醒来时灵光一现,突然想到这个问题的解决方案,特此记录。

问题描述

        在处理大型项目时,我遇到了一个棘手的架构问题。项目中的关键机制是:每次点击按钮都会创建一个长期运行的线程,而频繁的按钮点击会导致大量线程并行运作。这种设计带来了两个严重问题:一是公共资源的并发修改问题,二是某些需要独占的程序资源出现错误。

        起初,我尝试了简单的加锁和异步方案,但都未能完美解决问题。加锁方案存在明显缺陷:每次创建新线程时都需要对旧线程解锁并给新线程加锁。更糟糕的是,当新线程运行结束而尚未创建下一个新线程时,旧线程会自动解除阻塞继续运行,这与预期行为不符。最终,我找到了一种更简便高效的解决方案。

例子展示

        首先,我们先来看一个很简单的例子,代码如下

import threading
import time

def test(thread_count):
    global king_finance
    king_finance = king_finance / thread_count
    for i in range(500):
        print(f"这是第{thread_count}个线程的第{i+1}个数,皇帝的资产为{king_finance}")

def maintain():
    time.sleep(10)
        

if __name__ == '__main__':
    king_finance = 1000000000
    test_thread = threading.Thread(target=test, args=(1,), daemon=True)
    test_thread.start()
    test_thread.join(timeout=0.01)

    test_thread = threading.Thread(target=test, args=(2,), daemon=True)
    test_thread.start()
    test_thread.join(timeout=0.01)
    # 保证主线程不结束
    maintain()

        运行结果的部分截图如下

        由图可以看到,在超时时间timeout=0.01过后,线程test_thread和线程test+thread2会开始并发执行,很清楚的可以看到,如果没有线程2,线程1的皇帝的资产会一直都是1000000000,而当线程2与其并发执行的时候,将皇帝的资产修改为了500000000,直接减少了一半,这样就会导致,我在线程一运行的途中资源被线程2修改了。
        其次还有一个问题是,由于实际需要,我创建第二个调用同一个函数的线程时,并不希望前一个这个线程在继续运行,继续执行语句中的非共享资源,引起界面混乱。

        但当我实现了一个简易的锁来解决这个问题时,但是只能解决共享的资源在上锁时不能被其他线程所修改,并不能使他们只能其中一个来访问该函数中的其他非共享资源,代码如下

import threading
import time

def test(thread_count, lock):
    lock.acquire()
    global king_finance
    king_finance = king_finance / thread_count
    for i in range(500):
        print(f"这是第{thread_count}个线程的第{i+1}个数,皇帝的资产为{king_finance}")

def maintain():
    time.sleep(10)
        

if __name__ == '__main__':
    lock = threading.Lock()
    king_finance = 1000000000
    test_thread = threading.Thread(target=test, args=(1, lock, ), daemon=True)
    test_thread.start()
    test_thread.join(timeout=0.01)

    if lock.locked():
        lock.release()
    test_thread = threading.Thread(target=test, args=(2, lock, ), daemon=True)
    test_thread.start()
    test_thread.join(timeout=0.01)
    # 保证主线程不结束
    maintain()

        运行结果如下

        由图可知,确实可以有效的防止其他线程来同时修改公共资源,

        但是并不能阻止旧线程访问该函数的非共享资源,这并不是我想要的结果。但是利用好线程id就能非常完美的解决我的问题

        修改代码如下

import threading
import time

def test(thread_count, lock):
    lock.acquire()
    global king_finance, thread1_id, thread2_id
    king_finance = king_finance / thread_count
    for i in range(500):
        if thread2_id:
            if threading.current_thread().native_id == thread2_id:
                print(f"这是第{thread_count}个线程的第{i+1}个数,皇帝的资产为{king_finance}")
        else:
            print(f"这是第{thread_count}个线程的第{i+1}个数,皇帝的资产为{king_finance}") 


def maintain():
    time.sleep(10)
        

if __name__ == '__main__':
    lock = threading.Lock()
    thread2_id = 0
    thread1_id = 0
    king_finance = 1000000000
    test_thread = threading.Thread(target=test, args=(1, lock, ), daemon=True)
    thread1_id = test_thread.native_id
    test_thread.start()
    test_thread.join(timeout=0.01)

    if lock.locked():
        lock.release()
    test_thread = threading.Thread(target=test, args=(2, lock, ), daemon=True)
    thread2_id = test_thread.native_id
    test_thread.start()
    test_thread.join(timeout=0.01)
    # 保证主线程不结束
    maintain()

        其中结合了锁和一些逻辑,如果有新的线程建立,就只让新线程进入,其他的旧线程会直接退出,很好的实现了我所想要的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小z..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值