仿写的 singal slot的功能

import asyncio
import time
from threading import Thread, Lock


from typing import Any, Callable, Awaitable, Generic, TypeVar, ParamSpec
from types import MethodType
from weakref import WeakMethod, WeakSet

from threading import Lock


class MTC多子类单例(type):
    _lock_ = Lock()
    _instance_ = None

    def __call__(self, *args, **kwds):  # type: ignore

        if (
            self._instance_ is None
            or self._instance_.__class__.__name__ != self.__name__
        ):
            with self.__class__._lock_:
                if (
                    self._instance_ is None
                    or self._instance_.__class__.__name__ != self.__name__
                ):
                    self._instance_ = super().__call__(*args, **kwds)

        return self._instance_


class MTC单子类单例(MTC多子类单例):
    _lock_ = Lock()
    _instance_ = None
    _sub_cls_: list[type] = []

    def __new__(cls, a, b, c):
        if cls._sub_cls_:
            for tc in b:
                for ccc in cls._sub_cls_:
                    if tc is ccc or issubclass(tc, ccc):
                        raise RuntimeError("不能定义这个单例的多个子类", a, tc)
        cc = super().__new__(cls, a, b, c)
        cls._sub_cls_.append(cc)
        return cc


class SigServer(metaclass=MTC单子类单例):
    t0: float = 0

    class _run_wrap:
        def __init__(self, *args, **kw):
            self.args = args
            self.kw = kw

        def __call__(self, fn):
            return fn(*self.args, **self.kw)

    @classmethod
    def info(cls, *args):
        t1 = f"{time.time() - cls.t0:.4f}"

        print(t1, " ".join([str(i) for i in args]))

    def __init__(self):
        self.qu = asyncio.Queue()
        self.loop: asyncio.AbstractEventLoop
        self.running = False

    def stop(self):
        self.loop.call_soon_threadsafe(self.loop.create_task, self.qu.put(None))

    async def run(self):
        name = asyncio.current_task().get_name()  # type: ignore
        await self.qu.get()
        for task in asyncio.all_tasks():
            if task.get_name() != name:
                task.cancel()
        self.info("run over")

    def __call__(self):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
        self.running = True
        self.loop.run_until_complete(self.run())
        self.running = False
        self.loop.close()
        time.sleep(0.2)
        del self.loop

    def do_sync(self, fn, *args, **kw):
        if self.running:
            return self.loop.run_in_executor(None, self._run_wrap(*args, **kw), fn)

    def do_async(self, afn):
        if self.running:
            return self.loop.run_in_executor(None, self.loop.create_task, afn)

    def __enter__(self):
        self.__class__.t0 = time.time()
        self.thread = Thread(target=self, daemon=True)
        self.thread.start()
        time.sleep(0.005)
        self.info(f"start SigServer")
        return self

    def __exit__(self, a, b, c):
        self.stop()
        self.thread.join()
        # self.loop.close()
        pass


P = ParamSpec("P")


class Sig(Generic[P]):

    def __init__(self):
        self.mths: list[WeakMethod[Callable[P, Any]]] = []
        self.fns = WeakSet()
        self.lock = Lock()

    def connect(self, fn: Callable[P, Any]):
        with self.lock:
            if isinstance(fn, MethodType):
                self.mths.append(WeakMethod(fn))
            elif callable(fn):
                self.fns.add(fn)
            else:
                print("not callable")
        return self

    def __call__(self, *args: P.args, **kw: P.kwargs):
        dead = []
        for k in self.mths:
            fn = k()
            if fn:
                SigServer().do_sync(fn, *args, **kw)
            else:
                dead.append(k)
        if dead:
            with self.lock:
                for i in dead:
                    if i in self.mths:
                        self.mths.remove(i)
        for fn in self.fns:
            SigServer().do_sync(fn, *args, **kw)


if __name__ == "__main__":
    from threading import current_thread

    class test:

        def __init__(self) -> None:
            self.sg_f1 = Sig[float]()
            self.sg_f1.connect(self.on_f1)
            self.sg_f2 = Sig[float]()
            self.sg_f2.connect(self.on_f2)

        def on_f1(self, s: float):
            time.sleep(s / 10)
            SigServer.info(f"on f1   {s}", current_thread().name)

        def on_f2(self, s: float):
            time.sleep(s / 10 + 0.05)
            SigServer.info(f"on   f2 {s}", current_thread().name)

        def f1(self, i):
            self.sg_f1(i)

        def f2(self, i):
            self.sg_f2(i)

    with SigServer() as sv:

        tt = test()
        sv.info("start")

        for i in range(10, 15):
            tt.f1(i)
        for i in range(12, 17):
            tt.f2(i)

        time.sleep(3)

        sv.info("over")

posted @ 2025-05-12 11:49  方头狮  阅读(11)  评论(0)    收藏  举报