Python中线程实践的一些探索

遇到一个题,要求分3个线程,每个调用一个方法,实现0 1 0 2 0 3 0 4 0 5...这样的标准输出。

自己翻翻资料,利用 threading package,threading.Lock.acquire() 和 threading.Lock.release() 方法,对某些关键变量访问的限制(锁),可以实现。

from atexit import register
import threading
import time

lock = threading.Lock()

class Series_Output(object):
    def __init__(self, n: int):
        self.n = n
        self.i = 0
        self.zero_flag = True

    def zero(self):
        while self.i <= self.n:
            if self.zero_flag:
                lock.acquire()
                if True == self.zero_flag and self.i <= self.n:
                    if self.i < self.n:
                        print("0", end=" ")
                    self.zero_flag = False
                    self.i += 1
                lock.release()

    def even(self):
        while self.i <= self.n:
            if not self.zero_flag and self.i % 2 == 0:
                lock.acquire()
                if not self.zero_flag and self.i % 2 == 0 and self.i <= self.n:
                    print(self.i, end=" ")
                    self.zero_flag = True
                lock.release()

    def odd(self):
        while self.i <= self.n:
            if not self.zero_flag and self.i % 2 == 1:
                lock.acquire()
                if not self.zero_flag and self.i % 2 == 1 and self.i <= self.n:
                    print(self.i, end=" ")
                    self.zero_flag = True
                lock.release()


@register
def exit():
    print("\n所有线程执行完毕")
    print(f"ending time: {time.perf_counter()}")

if __name__ == "__main__":
    print(f"starting time: {time.perf_counter()}")
    s = Series_Output(20)
    threading.Thread(target=s.odd).start()
    threading.Thread(target=s.even).start()
    threading.Thread(target=s.zero).start()

结果如下。计算时间,可知需要5秒多。明显需要改进

starting time: 178479.0038989
0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 11 0 12 0 13 0 14 0 15 0 16 0 17 0 18 0 19 0 20 
所有线程执行完毕
ending time: 178484.8257394

请教豆包AI, 提供了用threading.condition 这样比较高级的调用。

import time
from atexit import register
import threading


class SeriesOutput(object):
    def __init__(self, n: int):
        self.n = n
        self.i = 0
        self.zero_flag = True
        self.lock = threading.Lock()
        self.condition = threading.Condition(self.lock)

    def zero(self):
        while self.i <= self.n:
            with self.condition:
                while not self.zero_flag:
                    self.condition.wait(timeout=1)
                if self.i <= self.n:
                    if self.i < self.n:
                        print("0", end=" ")
                    # print("0", end=" ")
                    self.zero_flag = False
                    self.i += 1
                self.condition.notify_all()


    def even(self):
        while self.i <= self.n:
            with self.condition:
                while self.zero_flag or self.i % 2!= 0:
                    self.condition.wait(timeout=1)
                if self.i <= self.n:
                    print(self.i, end=" ")
                    self.zero_flag = True
                self.condition.notify_all()

    def odd(self):
        while self.i <= self.n:
            with self.condition:
                while self.zero_flag or self.i % 2!= 1:
                    self.condition.wait(timeout=1)
                if self.i <= self.n:
                    print(self.i, end=" ")
                    self.zero_flag = True
                self.condition.notify_all()


@register
def exit():
    print("\n所有线程执行完毕")


if __name__ == "__main__":
    # print(f"starting time: {time.perf_counter()}")
    s = SeriesOutput(20)
    try:
        threading.Thread(target=s.odd).start()
        threading.Thread(target=s.even).start()
        threading.Thread(target=s.zero).start()
    except Exception as e:
        print(f"线程启动失败:{e}")

结果在屏幕上秒出,应该是毫秒级别的。但是程序却不退出。反复深入询问,都没有改善。

花了很多精力去troubleshooting, 也仔细阅读Python Threading 的文档,加了一下debug log,发现其实是 总是有个 odd 或 even 的进程还在等待,没有退出。锁已经是释放掉了,也试了主动在各处release()锁,python 都报错说没锁可以release。

调试的log输出代码为:

    time.sleep(2)
    print(f"\nFinally, s.i = {s.i}, s.zero_flag = {s.zero_flag}")
    print(f"Current thread status is:\n"
          f"threading.active_count() is {threading.active_count()};\n"
          f"threading.enumerate() is {threading.enumerate()};\n"
          f"s.lock.locked() is {s.lock.locked()}")

结果是

starting time: 194847.1397601
0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 11 0 12 0 13 0 14 0 15 0 16 0 17 0 18 0 19 0 20 
Finally, s.i = 21, s.zero_flag = False
Current thread status is:
threading.active_count() is 2;
threading.enumerate() is [<_MainThread(MainThread, started 18344)>, <Thread(Thread-2 (even), started 12596)>];
s.lock.locked() is False

root cause 是 s.i 到达 s.n 值以后,even() 或者 odd() 方法中的一个while 判断条件总为True, 导致 s.condition.wait() 一直等待,相应进程就不退出。

改写后代码如下:

import time
from atexit import register
import threading


class SeriesOutput(object):
    def __init__(self, n: int):
        self.n = n
        self.i = 0
        self.zero_flag = True
        self.lock = threading.Lock()
        self.condition = threading.Condition(self.lock)

    def zero(self):
        while self.i <= self.n:
            with self.condition:
                while not self.zero_flag:
                    self.condition.wait()
                if self.i <= self.n:
                    if self.i < self.n:
                        print("0", end=" ")
                    # print("0", end=" ")
                    self.zero_flag = False
                    self.i += 1
                self.condition.notify_all()




    def even(self):
        while self.i <= self.n:
            with self.condition:
                while self.zero_flag or (self.i <= self.n and self.i % 2!= 0):
                # while self.zero_flag or self.i % 2 != 0:
                    self.condition.wait()
                if self.i <= self.n:
                    print(self.i, end=" ")
                    self.zero_flag = True
                self.condition.notify()



    def odd(self):
        while self.i <= self.n:
            with self.condition:
                while self.zero_flag or (self.i <= self.n and self.i % 2!= 1):
                # while self.zero_flag or self.i % 2 != 1:
                    self.condition.wait()
                if self.i <= self.n:
                    print(self.i, end=" ")
                    self.zero_flag = True
                self.condition.notify()



@register
def exit():
    print("\n所有线程执行完毕")
    print(f"ending time: {time.perf_counter()}")

if __name__ == "__main__":
    print(f"starting time: {time.perf_counter()}")
    s = SeriesOutput(20)
    try:
        threading.Thread(target=s.odd).start()
        threading.Thread(target=s.even).start()
        threading.Thread(target=s.zero).start()
    except Exception as e:
        print(f"线程启动失败:{e}")

    # time.sleep(2)
    # print(f"\nFinally, s.i = {s.i}, s.zero_flag = {s.zero_flag}")
    # print(f"Current thread status is:\n"
    #       f"threading.active_count() is {threading.active_count()};\n"
    #       f"threading.enumerate() is {threading.enumerate()};\n"
    #       f"s.lock.locked() is {s.lock.locked()}")


结果如下。在5毫秒完成任务。比原始approach所需时间的1/1000。可以说,效率大大提高。

starting time: 195422.0831454
0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 11 0 12 0 13 0 14 0 15 0 16 0 17 0 18 0 19 0 20 
所有线程执行完毕
ending time: 195422.0887467

看来LLM prompt + 人工手动优化确实是下一步的工作常态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值