10

main函数基于libevent,但函数中有一个long run task。所以启动 N 个线程来运行任务。这个想法可以吗?以及如何在 C 中同时使用 libevent 和 pthread?

4

4 回答 4

3

碰到一个老问题,可能已经解决了。但是发布答案以防万一其他人需要它。

是的,在这种情况下可以进行线程化。我最近在 pthreads 中使用了 libevent,它似乎工作得很好。这是代码:

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

void * thread_func (void *);

int main(void)
{
    int32_t tid = 0, ret = -1;
    struct event_base *evbase;
    struct event *timer;
    int32_t *t_ret = &ret;
    struct timeval tv;

    // 1. initialize libevent for pthreads
    evthread_use_pthreads();

    ret = pthread_create(&tid, NULL, thread_func, NULL);
    // check ret for error

    // 2. allocate event base
    evbase = event_base_new();
    // 3. allocate event object
    timer = event_new(evbase, -1, EV_PERSIST, callback_func, NULL);
    // 4. add event
    tv.tv_sec = 0;
    tv.tv_usec = 1000;
    evtimer_add(timer, &tv);
    // 5. start the event loop
    event_base_dispatch(evbase);   // event loop

    // join pthread...

    // 6. free resources
    event_free(timer);
    event_base_free(evbase);
    return 0;
}

void * thread_func(void *arg)
{
    struct event *ev;
    struct event_base *base;

    base = event_base_new();
    ev = event_new(base, -1, EV_PERSIST, thread_callback, NULL);
    event_add(ev, NULL);  // wait forever
    event_base_dispatch(base);  // start event loop

    event_free(ev);
    event_base_free(base);
    pthread_exit(0);
}

如您所见,在我的例子中,主线程的事件是计时器。遵循的基本逻辑如下:

  1. 调用evthread_use_pthreads()为 Linux 上的 pthreads 初始化 libevent(我的例子)。对于 Windows evthread_use_window_threads()查看event.h本身给出的文档。
  2. 按照文档中的说明在全局堆上分配event_base结构。确保检查返回值是否有错误。
  3. 同上,但分配事件结构本身。就我而言,我没有等待任何文件描述符,因此 -1 作为参数传递。另外,我希望我的事件持续存在,因此EV_PERSIST。回调函数的代码被省略。
  4. 安排事件执行
  5. 启动事件循环
  6. 完成后释放资源。

在我的案例中使用的 Libevent 版本是libevent2 5.1.9,您还需要libevent_pthreads.so库进行链接。

干杯。

于 2017-12-21T05:12:05.290 回答
1

这篇博文中有一个多线程 libevent 示例: http ://www.roncemer.com/multi-threaded-libevent-server-example

他的解决方案是,引用:

解决方案是为每个活动连接创建一个 libevent 事件队列 (AKA event_base),每个连接都有自己的事件泵线程。这个项目正是这样做的,为您提供编写高性能、多线程、基于 libevent 的套接字服务器所需的一切。

于 2012-07-01T17:29:29.720 回答
1

那会奏效。

在 I/O 回调函数中,将耗时的工作委托给线程池的另一个线程。确切的机制取决于工作线程或线程池的接口。

要将结果从工作线程传回 I/O 线程,请使用管道。工作线程将指向结果对象的指针写入管道,然后 I/O 线程唤醒并从管道中读取指针。

于 2012-06-20T14:10:12.610 回答
0

注意这适用于 libev 而不是 libevent,但这个想法可能适用。

在这里,我为社区提供了一个示例。请评论并让我知道是否有任何明显的错误。此示例可能包括用于线程终止和将来优雅退出的信号处理程序。

//This program is demo for using pthreads with libev. 
//Try using Timeout values as large as 1.0 and as small as 0.000001
//and notice the difference in the output

//(c) 2009 debuguo
//(c) 2013 enthusiasticgeek for stack overflow
//Free to distribute and improve the code. Leave credits intact
//compile using:           gcc -g test.c -o test -lpthread -lev

#include <ev.h>
#include <stdio.h> // for puts
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t lock;
double timeout = 0.00001;
ev_timer timeout_watcher;
int timeout_count = 0;

ev_async async_watcher;
int async_count = 0;

struct ev_loop* loop2;

void* loop2thread(void* args)
{ 
    // now wait for events to arrive on the inner loop
    ev_loop(loop2, 0);
    return NULL;
}

static void async_cb (EV_P_ ev_async *w, int revents)
{
    //puts ("async ready");
    pthread_mutex_lock(&lock);     //Don't forget locking
    ++async_count;
    printf("async = %d, timeout = %d \n", async_count, timeout_count);
    pthread_mutex_unlock(&lock);   //Don't forget unlocking
}

static void timeout_cb (EV_P_ ev_timer *w, int revents) // Timer callback function
{
    //puts ("timeout");
    if(ev_async_pending(&async_watcher)==false){ //the event has not yet been processed (or even noted) by the event loop? (i.e. Is it serviced? If yes then proceed to)
        ev_async_send(loop2, &async_watcher); //Sends/signals/activates the given ev_async watcher, that is, feeds an EV_ASYNC event on the watcher into the event loop. 
    }

    pthread_mutex_lock(&lock);     //Don't forget locking
    ++timeout_count;
    pthread_mutex_unlock(&lock);   //Don't forget unlocking
    w->repeat = timeout;
    ev_timer_again(loop, &timeout_watcher); //Start the timer again.
}

int main (int argc, char** argv)
{
    if (argc < 2) {
        puts("Timeout value missing.\n./demo <timeout>");
        return -1;
    }
    timeout = atof(argv[1]);

    struct ev_loop *loop = EV_DEFAULT;  //or ev_default_loop (0);

    //Initialize pthread
    pthread_mutex_init(&lock, NULL);
    pthread_t thread;

    // This loop sits in the pthread
    loop2 = ev_loop_new(0);

    //This block is specifically used pre-empting thread (i.e. temporary interruption and suspension of a task, without asking for its cooperation, with the intention to resume that task later.)  
    //This takes into account thread safety
    ev_async_init(&async_watcher, async_cb);
    ev_async_start(loop2, &async_watcher);
    pthread_create(&thread, NULL, loop2thread, NULL);

    ev_timer_init (&timeout_watcher, timeout_cb, timeout, 0.); // Non repeating timer. The timer starts repeating in the timeout callback function
    ev_timer_start (loop, &timeout_watcher);

    // now wait for events to arrive on the main loop
    ev_loop(loop, 0);
    //Wait on threads for execution
    pthread_join(thread, NULL);

    pthread_mutex_destroy(&lock);
    return 0;
}
于 2013-02-08T17:50:58.193 回答