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")