在Ubuntu16.04中, libevent的默认安装位置为
#在 trusty 发行版中 amd64 硬件架构下的 libevent-2.0-5 软件包文件清单 /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5 /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9 /usr/share/doc/libevent-2.0-5/changelog.Debian.gz /usr/share/doc/libevent-2.0-5/copyright
https://github.com/libevent/libevent/blob/master/sample/time-test.c
这是一个定时触发事件的例子, 根据输入参数的不同, 单次执行或持续执行.
在main方法中, 通过以下的方法调用将timeout_event加入到将来的事件中.
base = event_base_new();
struct event timeout; event_assign(&timeout_event, base, -1, flags, timeout_callback, (void *) &timeout_event); event_add(timeout_event, &tv); event_base_dispatch(base);
将timeout_event作为参数传入callback方法, 然后在callback方法中再次将timeout_event加入到将来的事件中
struct event *timeout_event = arg; // ... event_add(timeout_event, &tv);
在这种情况下, 原代码中的flags = EV_PERSIST其实是不起作用的, 因为callback中复用了原来的内存地址, 设置为flags = 0一样能持续执行.
如果将event_assign改为event_new, 则调用变为
base = event_base_new(); // 注意此处不传入event而传入event_base, 因为event在当前方法中new并alloca空间, 所以传入的event指针是无效的. struct event *timeout_event = event_new(base, -1, flags, timeout_callback, base); event_add(timeout_event, &tv); evutil_gettimeofday(&lasttime, NULL); event_base_dispatch(base);
而callback中的处理变为
struct event_base *base = arg; struct event *timeout_event = event_new(base, -1, 0, timeout_callback, base); event_add(timeout_event, &tv);
这时候, 如果代码中使用flags = EV_PERSIST就会产生内存泄漏, 因为在触发后并不释放, 会持续的每隔1秒调用callback, 这时候要么在callback中不新增event, 要么flag设为0.
修改后的代码:
/* * XXX This sample code was once meant to show how to use the basic Libevent * interfaces, but it never worked on non-Unix platforms, and some of the * interfaces have changed since it was first written. It should probably * be removed or replaced with something better. * * Compile with: * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent */ #include <sys/types.h> #ifndef _WIN32 #include <sys/queue.h> #include <unistd.h> #endif #ifdef EVENT__HAVE_SYS_TIME_H #include <sys/time.h> #endif #include <stdio.h> #include <string.h> #include <event2/event.h> #include <event2/event_struct.h> #ifdef _WIN32 #include <winsock2.h> #endif struct timeval lasttime; int event_is_persistent; static void timeout_callback(evutil_socket_t fd, short event, void *arg) { struct timeval newtime, difference; struct event_base *base = arg; double elapsed; // The evutil_gettimeofday function sets tv to the current time. The second argument is unused. evutil_gettimeofday(&newtime, NULL); // This macro subtract (respectively) their first two arguments, and stores the result in the third. evutil_timersub(&newtime, &lasttime, &difference); elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6); printf("timeout_callback called at %d: %.3f seconds elapsed. ", (int) newtime.tv_sec, elapsed); lasttime = newtime; if (event_is_persistent == 1) { struct event *timeout_event = event_new(base, -1, 0, timeout_callback, base); struct timeval tv; evutil_timerclear(&tv); tv.tv_sec = 0; tv.tv_usec = 10000; event_add(timeout_event, &tv); } } int main(int argc, char **argv) { struct timeval tv; struct event_base *base; int flags; // strcmp: <0 the first character that does not match has a lower value in ptr1 than in ptr2 // 0 the contents of both strings are equal // >0 the first character that does not match has a greater value in ptr1 than in ptr2 if (argc == 2 && !strcmp(argv[1], "-p")) { event_is_persistent = 1; // Use this if you use event_new instead of event_assign flags = 0; } else { event_is_persistent = 0; flags = 0; } /* Initalize the event library */ base = event_base_new(); /* * Prepare a new, already-allocated event structure to be added. * The function event_assign() prepares the event structure ev to be used in future calls to event_add() and event_del(). * Unlike event_new(), it doesn't allocate memory itself: it requires that you have already allocated a struct event, * probably on the heap. Doing this will typically make your code depend on the size of the event structure, and thereby * create incompatibility with future versions of Libevent. * The easiest way to avoid this problem is just to use event_new() and event_free() instead. * A slightly harder way to future-proof your code is to use event_get_struct_event_size() to determine the required size * of an event at runtime. * Note that it is NOT safe to call this function on an event that is active or pending. Doing so WILL corrupt internal * data structures in Libevent, and lead to strange, hard-to-diagnose bugs. You can use event_assign to change an existing * event, but only if it is not active or pending! */ //event_assign(&timeout_event, base, -1, flags, timeout_callback, (void *) &timeout_event); struct event *timeout_event = event_new(base, -1, flags, timeout_callback, base); // Set (tvp)->tv_sec and (tvp)->tv_usec to 0 evutil_timerclear(&tv); tv.tv_sec = 1; // Add an event to the set of pending events. // The function event_add() schedules the execution of the ev event when the event specified in event_assign()/event_new() occurs, // or when the time specified in timeout has elapesed. If atimeout is NULL, no timeout occurs and the function will only be called // if a matching event occurs. The event in the ev argument must be already initialized by event_assign() or event_new() and may // not be used in calls to event_assign() until it is no longer pending. // If the event in the ev argument already has a scheduled timeout, calling event_add() replaces the old timeout with the new one, // or clears the old timeout if the timeout argument is NULL. event_add(timeout_event, &tv); evutil_gettimeofday(&lasttime, NULL); event_base_dispatch(base); return (0); }
Update 2017-12-05:
如果使用的是libevent 2.1.1以后的版本, 可以使用event_self_cbarg()方法. 在event_new中通过 event_self_cbarg() 方法传入自身指针.
#include <event2/event.h> static int n_calls = 0; void cb_func(evutil_socket_t fd, short what, void *arg){ struct event *me = arg; printf("cb_func called %d times so far. ", ++n_calls); if (n_calls > 100) event_del(me); } void run(struct event_base *base){ struct timeval one_sec = { 1, 0 }; struct event *ev; /* We're going to set up a repeating timer to get called called 100 times. */ ev = event_new(base, -1, EV_PERSIST, cb_func, event_self_cbarg()); event_add(ev, &one_sec); event_base_dispatch(base); }