11

これは少し理論的な質問です。センサーでいっぱいのデバイスを想像してみてください。ここで、センサー x が何かを検出した場合、何かが起こるはずです。一方、2 つのセンサーが 2 つの異なるものを検出するなど、他の何かが検出された場合、このデバイスは異なる動作をする必要があります。

webdesign (so javascript) から、たとえば (jquery を使用して)$(".x").on("click", function(){})または angularjs からイベントについて学びまし$scope.watch("name_of_var", function())た。

複雑なライブラリを使用せずに、C でこの動作を再現する可能性はありますか?

ありがとう。

4

4 に答える 4

7

私が考えることができるシステムは、加入者通知モデルです。センサーを処理するものがある場合があります(たとえば、何かが発生したかどうかを確認するためにセンサーをポーリングするスレッド)。何かを検出すると、タスクは外界に認識させるためのメカニズムを起動する必要があります。これが通知プロセスです。
反対に、センサーに関心のある人だけに通知する必要があるため、サブスクリプション方式を使用してこれを処理する必要があります。

さて、難しい部分が入ります。センサーハンドラーが世界に通知するとき、それを行うのにあまり時間をかけないでくださいそうしないと、他のイベントを見逃す可能性があります。したがって、通知プロセス専用のタスク(またはスレッド)を用意する必要があります。一方、加入者は、そのような通知されたイベントが受信されたときに、データの一部を更新することを望んでいます。これは明らかに非同期プロセスであるため、サブスクライバーは通知スレッドにコールバックを提供する必要があります。
最後に、イベントにタイムスタンプを付ける必要があります。これにより、受信者は、取得したイベントが古くなっているかどうか、およびそれを破棄する必要があるかどうかを知ることができます。
最後のものは、以下のコードのように見えるかもしれません:


データ構造

/*
 * Some data structures to begin with
 */
struct event;
struct notifier;
struct subscription;
struct notify_sched;


typedef int (*notify_cbck)(struct event *evt, void *private);
/*
 *@type : a value to show the type of event
 *@t : the timestamp of the event
 *@value : a pointer towards the event data
 */
struct event {
    int type;
    struct timeval t; // the timestamp
    void *value;
};

/*
 * @type : the type in which the subscriber is interested
 * @cb : the callback that should be run when an event occur
 * @cb_data : the data to provide to the callback
 * @next,prev : doubly-linked list
 */
struct subscription {
    int type;
    notify_cbck cb;
    void *cb_data;
    struct subscription *next, *prev;
};

/*
 * This structure gathers the subscriptions of a given type.
 * @type : the event type
 * @subs : the subscription list
 * @mutex : a mutex to protect the list while inserting/removing subscriptions
 * @next,prev : link to other typed subscriptions
 */

struct typed_subscription {
    int type;
    struct subscription *subs;
    mutex_t mutex;
    struct typed_subscription *next, *prev;
};

/*
 * @magic : the ID of the event producer
 * @t_subs : the typed_subscription list
 * @mutex : a mutex to protect data when (un)registering new types to the producer
 * @next, prev : doubly-linked list ...
 */
struct notifier {
    int magic;
    struct typed_subscription *t_subs;
    mutex_t mutex;
    struct notifier *next, *prev;
};

/*
 * @ntf : the notifiers list
 * @mutex : a mutex to protect the ntf list
 * @th : something to identify the task that hosts the scheduler
 */
struct notify_sched {
    struct notifier *ntf;
    mutex_t mutex;
    pthread_t th; // I assume it's a classic pthread in this example.
};

現在、回答を完了する時間がありません。後で完全な例を示すために編集します。しかし、データ構造から始めて、いくつかのアイデアを得る必要があります。とにかくこれが少し役立つことを願っています。

于 2013-01-26T14:30:46.953 に答える
4

別のスレッドで割り込みまたはメジャーイベントループにアクセスできる組み込みシステムを所有していると仮定します。そうでない場合、これは不可能です。

イベント処理の基本モデルは次のとおりです。

#define NOEVENT 0

typedef void *(*EventHandler)(void *);

void *doNothing(void *p){/*do nothing absolutely*/ return NULL; }
typedef struct _event{
  EventHandler handler;
}Event, *PEvent;

Event AllEvents[1000];
unsigned short counter = 0;
void InitEvents()
{
    LOCK(AllEvents);
    for(int i = 0; i < 1000; i++){ 
        AllEvents[i].handler = doNothing;
    }
    UNLOCK(AllEvents);
}
void AddEvent(int EventType, EventHandler ev_handler)
{
    LOCK(AllEvents);
    AllEvents[EventType].handler = ev_handler;
    UNLOCK(AllEvents);
}

void RemoveEvent(int EventType, EventHandler ev_handler)
{
   LOCK(AllEvents);
   AllEvents[EventType] = doNothing;
   UNLOCK(AllEvents); /*to safeguard the event loop*/
}

/*to be run in separate thread*/
void EventLoop()
{
   int event = NOEVENT;
   EventHandler handler;
   while(1){
       while(event == NOEVENT)event=GetEvents();
       handler = AllEvents[event].handler;
       handler();/*perform on an event*/
  }
}

これが少しナイーブなら申し訳ありませんが..しかし、これは私が現時点で考えることができる最高のものです。

于 2013-01-26T13:43:53.100 に答える
0

イベントの場合、実際のイベント (たとえば、ネットワークからのデータ) が発生したことを検出するイベント ループが必要です。次に、ソフトウェア イベント構造を生成し、適切なイベント ハンドラーを呼び出します。より複雑なシステムでは、イベント ハンドラーのチェーンをハンドラーまで呼び出します。受け入れられたイベントをマークします。ハンドラーがない場合、またはイベントを受け入れる登録済みハンドラーがない場合、イベントは無視されます。

多くの場合、イベント ループは、ライブラリ自体が生成する可能性のあるイベントに加えて、アプリケーションがイベント ハンドラーを持ち、アプリケーション固有のイベントを送信するための API を持つライブラリ内にあります。イベント ベースのアプリケーションの問題の 1 つは、ライブラリ開発者がライブラリ自身のイベント ループ以外のイベント ループを使用できるように細心の注意を払っていない限り、両方とも独自のイベント ループを必要とする 2 つのライブラリを使用するのはしばしば複雑になることです。

非常に低レベルのリアルタイム システムでない限り、イベント ループがビジー待機を行わないことが重要です。Linux/Unix/Posix コードでは、イベント ループは通常、select() または poll() システム関数の周りで機能します。イベントがない場合、イベント ループは次のタイマー イベントの時間に一致するタイムアウトでこの関数を呼び出します (タイマー イベントがある場合)。タイムアウトに加えて、select()/poll() は、指定されたファイル ハンドル (多くの場合、ネットワークまたは IPC ソケット) のいずれかが読み取り/書き込み/エラーの準備ができている場合、および他の方法で処理されない割り込みがある場合にも戻ります。 . 次に、イベント ループ コードは、関数が返された理由をチェックし、必要なイベントを生成してディスパッチし、すべてが完了すると、select()/poll() 関数を再度呼び出します。

また、イベント ベースのシステムで重要なことは、イベント ハンドラーはイベント ループによって呼び出される関数であるため、ブロックしてはならないということです。そのため、イベント ループはバックグラウンドのどこかで続行されず、ハンドラー関数呼び出しはイベント ループの一部です。したがって、ハンドラー関数は、利用可能なデータのみを、できれば迅速に処理し、必要な状態を保存して後で続行し、次のイベントを待機するために戻る必要があります。ブロックする必要がある操作については、別のスレッドを起動する必要があります。また、長い計算の場合、イベント ループも実行できるように計算を細かく分割するか、別のスレッドで計算を実行する必要があります。GUI アプリケーションのタイトル バーに表示される「応答なし」という迷惑なメッセージは、通常、これを意味します。アプリケーション プログラマーが怠惰で無能であり、イベント ループをブロックしたため、OS イベントに反応できません。

そのため、C でイベント ベースのシステムを作成するのは非常に簡単です。select()/poll() を含むループを作成し、イベント タイプを定義し、イベントのデータ構造体を作成し、関数ポインタのリストを作成するだけです。イベントタイプごとに、新しいイベント構造体をパラメーターとして呼び出します。

于 2013-01-26T14:05:19.410 に答える
0

このメッセージが見つかり、解決策が正確に見つからなかったため、https://prdeving.wordpress.com/2017/04/03/event-driven-programming-with-c-89/に基づいてコードをここに投稿します。ほかの人のため。

event.h

typedef struct s_Arguments {
    char *name;
    int value;
} Arguments; // Treat this as you please
 
typedef struct s_Event {
    char *name;
    Arguments args;
} Event;
 
typedef struct {
    char *name;
    void (*handler)(void*);
} Handler;

struct s_EventContainer {
    int *exitFlag;
    Event *poll;
    Handler *listeners;
    void (*registerEvent)(char *name, void*);
    void (*emit)(char *name, Arguments args);
    int listenersc;
    int pollc;
};

struct s_EventContainer eventContainer;

void _registerEvent(char *name, void*);
void _emitEvent(char *name, Arguments args);

void popPoll();

void find_and_exec_handler_in_listeners(char *name, Arguments *args);

void * fn_eventsDigest(void * p_Data);

void *testFired(Arguments *args);

event.c

#include "event.h"


int BLOCK_POP, BLOCK_EMIT;


void _registerEvent(char *name, void *cb) {
    LOG("_registerEvent '%s'", name);
    if (!eventContainer.listeners) eventContainer.listeners = malloc(sizeof(Handler));    
    Handler listener = {name, cb};  
    eventContainer.listeners[eventContainer.listenersc] = listener;  
    eventContainer.listenersc++;  
    eventContainer.listeners = realloc(eventContainer.listeners, sizeof(Handler) * (eventContainer.listenersc+1));  
}

void _emitEvent(char *name, Arguments args) {
    LOG("Emit event '%s' with args value %d", name, args.value);
    while(BLOCK_EMIT) asm("nop");
    BLOCK_POP = 1;
    if (!eventContainer.poll) eventContainer.poll = malloc(sizeof(Event));    
    Event poll = {name, args};  
    eventContainer.poll[eventContainer.pollc] = poll;  
    eventContainer.pollc++;  
    eventContainer.poll = realloc(eventContainer.poll, sizeof(Event) * (eventContainer.pollc+1));  
    BLOCK_POP = 0;
}

void popPoll(){
    int* temp = malloc((eventContainer.pollc - 1) * sizeof(Event));
    memcpy(temp, eventContainer.poll+1, (eventContainer.pollc - 1) * sizeof(Event));
    eventContainer.pollc -= 1;
    eventContainer.poll = realloc(eventContainer.poll, eventContainer.pollc * sizeof(Event) + sizeof(Event));
    memcpy(eventContainer.poll, temp, eventContainer.pollc * sizeof(Event));
    temp = NULL;
}



void find_and_exec_handler_in_listeners(char *name, Arguments *args){
    int i;
    for (i=0; i < eventContainer.listenersc; i++) {
        if (strcmp(eventContainer.listeners[i].name, name ) == 0 ) {
            (*eventContainer.listeners[i].handler)(args);
        }
    }
}


void * fn_eventsDigest(void * p_Data) {
    struct s_EventContainer *eventContainer = (struct s_EventContainer *)p_Data;

    BLOCK_POP = 0;
    BLOCK_EMIT = 0;
    
    while(*(eventContainer->exitFlag) == 0) {
        if ( eventContainer->pollc > 0 && BLOCK_POP == 0) {
            BLOCK_EMIT = 1;
            Event ev = eventContainer->poll[0];
            find_and_exec_handler_in_listeners(ev.name, &ev.args);
            popPoll(); 
            BLOCK_EMIT = 0;
        }
    usleep(1*1000);
    }
    printf("Event Loop::exiting...\r\n"); fflush(stdout);
}

void *testFired(Arguments *args) {
    LOG("test event fired with value %d \r\n", args->value);
}

main.c

#include "event.h"
#include <pthread.h>

struct s_EventContainer eventContainer = {
    &Data.exitFlag, //exit loop
    NULL, //poll
    NULL, //listeners
    _registerEvent,
    _emitEvent,
    0,
    0
};

pthread_t events_thread;

int main(int argc, char** argv) {

    eventContainer.registerEvent("test", &testFired);
    
    int ret = pthread_create (&events_thread, NULL, fn_eventsDigest, &eventContainer);
    if (ret) {
        fprintf (stderr, "%s", strerror (ret));
    }
    pthread_setname_np(events_thread,"events_thread");
   
    //....
    sleep(2);
    Arguments args;
    args.name = "test";
    args.value = 10;
    eventContainer.emit("test", args);

    pthread_join (events_thread, NULL);
    return (EXIT_SUCCESS);
}

私は提案を受け入れます。

于 2020-08-28T16:01:43.830 に答える