一点一滴成长

导航

libuv线程

1、概述

  libuv的线程API与Linux的pthread的API在使用方法和语义上很接近,因为要跨平台,所以libuv支持的线程API个数很有限。libuv中只有一个主线程,主线程上只有一个event loop。如下为创建线程的一个简单示例:

#include <stdio.h>
#include <uv.h>

void thread_fun(void* arg) { //线程方法
    fprintf(stdout, "thread start\n");
    int param = *((int*)arg);
}

int main() {
    int param = 100;
    uv_thread_t thread_id;
    uv_thread_create(&thread_id, thread_fun, &param); //创建线程并开始

    uv_thread_join(&thread_id); //等待线程结束
    return 0;
}

2、线程同步

  ①、互斥量

UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); //执行成功返回0,错误返回错误码
UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); //调试模式下出错会引发abort()中断,而不是返回错误码
UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); //libuv的API中并未实现递归上锁
UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle);
UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle);

  ②、读写锁

int    uv_rwlock_init(uv_rwlock_t* rwlock);
void uv_rwlock_rdlock(uv_rwlock_t* rwlock); //
int    uv_rwlock_tryrdlock(uv_rwlock_t* rwlock);
void uv_rwlock_rdunlock(uv_rwlock_t* rwlock);
void uv_rwlock_wrlock(uv_rwlock_t* rwlock); //
int    uv_rwlock_trywrlock(uv_rwlock_t* rwlock);
void uv_rwlock_wrunlock(uv_rwlock_t* rwlock);
void uv_rwlock_destroy(uv_rwlock_t* rwlock);

  ③、屏障

  libuv中屏障相关方法分别对应linux中pthread_xxx()相关方法的功能,“屏障”就相当于是线程的栏杆。可以将多个线程挡在同一栏杆前,直到所有线程到齐,然后撤下栏杆同时放行。

int uv_barrier_init(uv_barrier_t* barrier, unsigned int count); //初始化屏障并指定要等待的线程个数
int uv_barrier_wait(uv_barrier_t* barrier); //在线程中调用该方法,告诉屏障我已经到达栏杆处了,然后屏障会检查是否所有线程都已经到达,否的话已经到达栏杆的线程会阻塞,是的话放行所有线程
void uv_barrier_destroy(uv_barrier_t* barrier); //释放屏障

  ④、其它

  libuv同样支持信号量,条件变量,而且API的使用方法和pthread中的用法很类似,具体可以参考libuv文档

  libuv中还提供一个uv_once()方法,在多个线程中通过uv_once来调用指定方法的话,该方法只会被一个线程所调用,如下所示两个线程执行完毕后g_i的值是1:

uv_once_t once_only = UV_ONCE_INIT;
int g_i = 0;

void increment() {
    i++;
}

void thread1() {
    /* ... work */
    uv_once(&once_only, increment);
}

void thread2() {
    /* ... work */
    uv_once(&once_only, increment);
}

 3、TLS(线程局部存储)

  TLS就是只能被线程内的各个方法访问的变量,该变量不能被其它线程访问。如下为libuv中TLS相关方法,具体可以参考libuv文档

int uv_key_create(uv_key_t* key) //对应pthread中的pthread_key_create()
void uv_key_delete(uv_key_t* key)
void* uv_key_get(uv_key_t* key)
void uv_key_set(uv_key_t* key, void* value)

 4、任务队列

  uv_queue_work()会使用线程池执行指定的任务(线程池默认大小为4,可以通过环境变量UV_THREADPOOL_SIZE来设置),而且当任务完成后会在主线程中运行指定的回调。uv_queue_work()的第二个参数需要一个uv_work_t(任务方法和完成通知回调方法的参数),可以通过uv_work_t::data域来传递上下文数据,如下所示将uv_work_t放到上下文数据结构中的话,可以一次malloc把uv_work_t和数据都申请了出来。

  uv_cancel()可以取消工作队列中的任务,其参数为uv_work_t。只有还未开始的任务可以被取消,此时回调通知方法中的status参数值为UV_ECANCELED,如果任务已经开始执行或者执行完毕,uv_cancel()返回0。uv_cancel()同样可以用在uv_fs_t和uv_getaddrinfo_t请求上。

#include <stdio.h>
#include <uv.h>

struct SData {
    uv_work_t req;
    char* str;
    int n = 0;
};

void fun(uv_work_t* req) {
    SData* pBaton = (SData*)req->data;

}

void after_fun(uv_work_t* req, int status) {
    if(status == UV_ECANCELED) { //任务被取消

    }
    else {

    }

    SData* pBaton = (SData*)req->data;
    free(pBaton->str);
    free(pBaton);
}

int main() {  
    uv_loop_t* loop = uv_default_loop();

    SData* pBaton = (SData*)malloc(sizeof(SData));
    pBaton->req.data = (void*)pBaton;
    pBaton->str = _strdup("str");
    uv_queue_work(loop, &pBaton->req, fun, after_fun);
   
    return uv_run(loop, UV_RUN_DEFAULT);
}

5、异步调用 

  使用uv_async_init()和uv_async_send()可以异步的执行一个方法,该方法会在event-loop主线程中执行。uv_async_send()相当于是向主线程发了一个消息,主线程收到消息后会执行uv_async_init()设置的方法。有可能多次调用uv_async_send后只运行了一次回调函数:比如你调用了两次uv_async_send(), 而 libuv很忙,暂时还没有机会运行回调函数。

#include <stdio.h>
#include <uv.h>

uv_async_t async; //异步对象
uv_work_t req;

void thread_fun(uv_work_t* req) {

    async.data = (void*)_strdup("data");
    uv_async_send(&async); //执行异步对象
}

void after_thread_fun(uv_work_t* req, int status) {
   
    uv_close((uv_handle_t*)&async, NULL); //释放异步对象,任务完成的回调after_thread_fun()会在异步方法async_fun()之后执行
}

void async_fun(uv_async_t* handle) {
    char* pData = (char*)handle->data;

    free(pData);
}

int main() {  
    uv_loop_t* loop = uv_default_loop();

    uv_async_init(loop, &async, async_fun); //初始化异步对象

    uv_queue_work(loop, &req, thread_fun, after_thread_fun);
   
    return uv_run(loop, UV_RUN_DEFAULT);
}

 

posted on 2024-05-13 16:44  整鬼专家  阅读(165)  评论(0编辑  收藏  举报