How to understand event driven? What scene is suitable for use? How does Python implement an event listener?

Posted by crisward on Tue, 01 Feb 2022 19:09:52 +0100

The little sister of the product gave a gong such business requirements: the game server hopes to trigger a variety of effects when players upgrade or achieve a certain achievement. For example, after the player is upgraded, the full attributes will be improved. Unlock the new system, you can learn other skills and start a new combat mode... It can be seen that after the player is upgraded, all linked businesses should be concentrated and processed in turn. First of all, we can use if players to upgrade and then add business function codes one by one. However, the code written in this way has a high degree of coupling, which often "affects the whole body". Once the subsequent new business is added, we have to continue to insert the code.

In order to achieve the decoupling effect, the event driven model is introduced: we wrap the "player upgrade" into an event, and any "Observer" interested in this event only needs to listen and execute the corresponding logic. Event driven model is what we often call publish subscribe model. Event driven mainly includes these three types of elements: event source, event listener and event object; The corresponding operation functions need to include: listening for actions, sending events, and calling the listener response function. Let's experience the following event driven through an event listener for publishing and subscribing:

# -*- coding: utf-8 -*-
from queue import Queue, Empty
from threading import *


class EventManager:
    def __init__(self):
        """Initialize event manager"""
        # Event object list
        self.__eventQueue = Queue()
        # Event manager switch
        self.__active = False
        # Event handling thread
        self.__thread = Thread(target=self.__run)
        self.count = 0
        # Save the response function of the corresponding event
        self.__handlers = {}

    def __run(self):
        print('{}_Run'.format(self.count))
        while self.__active == True:
            try:
                event = self.__eventQueue.get(block=True, timeout=1)
                self.__eventProcess(event)
            except Empty:
                pass
            self.count += 1

    def __eventProcess(self, event):
        print('{}_EventProcess'.format(self.count))
        if event.type_ in self.__handlers:
            for handler in self.__handlers[event.type_]:
                handler(event)
        self.count += 1

    def start(self):
        print('{}_Start'.format(self.count))
        self.__active = True
        self.__thread.start()
        self.count += 1

    def stop(self):
        print('{}_Stop'.format(self.count))
        self.__active = False
        self.__thread.join()
        self.count += 1

    def addEventListener(self, type_, handler):
        print('{}_addEventListener'.format(self.count))
        try:
            handlerList = self.__handlers[type_]
        except KeyError:
            handlerList = []
            self.__handlers[type_] = handlerList
        if handler not in handlerList:
            handlerList.append(handler)
        print(self.__handlers)
        self.count += 1

    def removeEventListener(self, type_, handler):
        print('{}_removeEventListener'.format(self.count))
        try:
            handlerList = self.handlers[type_]
            if handler in handlerList:
                handlerList.remove(handler)
            if not handlerList:
                del self.__handlers[type_]
        except KeyError:
            pass
        self.count += 1

    def sendEvent(self, event):
        print('{}_SendEvent'.format(self.count))
        self.__eventQueue.put(event)
        self.count += 1


class Event:
    def __init__(self, type_=None):
        self.type_ = type_
        self.dict = {}

Take official account of publish and subscribe as an example to build test classes.

# -*- coding: utf-8 -*-
from eventManager import *

EVENT_ARTICAL = "Event_Artical"


class PublicAccounts:
    def __init__(self, eventManager):
        self.__eventManager = eventManager

    def WriteNewArtical(self):
        event = Event(type_=EVENT_ARTICAL)
        event.dict["artical"] = u'Python Implement an event listener\n'

        self.__eventManager.sendEvent(event)
        print(u'"Shallow daily Python"The official account sends new articles.\n')


class Listener:
    def __init__(self, username):
        self.__username = username

    def ReadArtical(self, event):
        print(u'%s Received article' % self.__username)
        print(u'Reading new article:%s' % event.dict['artical'])


def test():
    listener1 = Listener("Antonia")
    listener2 = Listener("Steve")
    listener3 = Listener("JOJO")
    eventManager = EventManager()
    eventManager.addEventListener(EVENT_ARTICAL, listener1.ReadArtical)
    eventManager.addEventListener(EVENT_ARTICAL, listener2.ReadArtical)
    eventManager.addEventListener(EVENT_ARTICAL, listener3.ReadArtical)
    eventManager.start()

    publicAcc = PublicAccounts(eventManager)
    timer = Timer(2, publicAcc.WriteNewArtical)
    timer.start()


if __name__ == '__main__':
    test()

The results of code operation are as follows

0_addEventListener
{'Event_Artical': [<bound method Listener.ReadArtical of <__main__.Listener instance at 0x1075f5758>>]}
1_addEventListener
{'Event_Artical': [<bound method Listener.ReadArtical of <__main__.Listener instance at 0x1075f5758>>, <bound method Listener.ReadArtical of <__main__.Listener instance at 0x1075f5830>>]}
2_addEventListener
{'Event_Artical': [<bound method Listener.ReadArtical of <__main__.Listener instance at 0x1075f5758>>, <bound method Listener.ReadArtical of <__main__.Listener instance at 0x1075f5830>>, <bound method Listener.ReadArtical of <__main__.Listener instance at 0x1075f5878>>]}
3_Start
4_Run
5_SendEvent
"Shallow daily Python"The official account sends new articles.

6_EventProcess
Antonia Received article
 Reading new article: Python Implement an event listener

Steve Received article
 Reading new article: Python Implement an event listener

JOJO Received article
 Reading new article: Python Implement an event listener

addEventListener is used to bind the event to the listener and register the processor of the event into the processor list; Start start the event manager and start the event processing thread; Run the engine and set the event blocking time to 1 second; SendEvent sends events and stores events in the event queue; EventProcess is used to process events and check whether there is a processing function for listening to the event. If there is, the events will be passed to the processing function for execution in sequence.

What scenarios are suitable for event driven? Generally speaking, "multi task processing without synchronous processing can use event driven". So what is the relationship between event driven and single thread and multithreading?

  • Single thread: tasks in the synchronization model are executed in sequence. If one task is blocked due to I/O, all other tasks need to wait until it is completed, that is, they need to be serialized.

  • Multithreading: multiple threads are managed by the operating system. They can be processed in parallel on multiprocessor systems or interleaved on single processor systems. This allows other threads to continue executing while one thread blocks a resource. However, the disadvantage is that additional code needs to be written to protect shared resources and prevent simultaneous access by multiple threads. Thread synchronization mechanisms such as lock, reentrant function, thread local storage or other mechanisms need to be introduced to deal with thread safety problems.

  • Event driven: in a separate thread control, when processing I/O operations, register a callback into the event loop. The callback describes how to process an event, and then continue to execute when the I/O operation is completed. The event loop polls all events and assigns them to pending events to callback functions when they arrive. No additional threads are required and there is no need to worry about thread safety.

 

reference resources:

https://blog.csdn.net/brucewong0516/article/details/84031715

https://criss.blog.csdn.net/article/details/85000570

 

Topics: Python