介绍一种思路,可以减少锁操作,不是绝对的无锁,但可以大大降低锁的数量。真正的无锁技术后续专门更文。
为什么要使用无锁技术?
为了高效,锁的开销很大,会导致性能下降。(后续更文介绍为什么锁的开销大)
锁的逻辑和使用:多个线程读写共同资源(称作:竞争资源),为保持同步(即,防止一起操作造成混乱),独占资源并使用。A拿到该资源即上锁,B线程再来操作该资源,因拿不到锁而被迫等待。A用完资源释放锁,此时其他线程才有机会操作该资源。
这样,造成的问题就是低效,线程拿到时间片却拿不到资源,于是阻塞等待。
一种解决方法,是N个线程竞争一个资源时,启动代理线程代为处理,N个线程向代理线程排队。
EVL { // 这是一个线程实例。当前只使用defer_mgr完成思路展示。
defer_mgr // defer队列,操作竞争资源,就将请求发送到这个队列即可。
// timer_mgr // 定时相关的请求,在这里排队。
// poll_mgr // sock相关的请求,在这里排队。
int exit_req; // 开关。
}
创建EVL: Evl_Create()
顶层结构(第一层结构)。
这是线程的句柄,来自各线程的任务在指定队列排队。
初始化EVL: Evl_Init(evl)
创建并初始化 defer_mgr 队列。
启动EVL服务:Evl_Run(evl)
开启一个线程执行该函数,遍历各队列,有任务则处理。
调用Evl_ProcessMgrs执行EVL各个队列中任务。
Evl_Run -- Evl_ProcessMgrs -- DeferMgr_Process
退出EVL: Evl_Exit(evl)
销毁EVL: Evl_Destroy(evl)
创建一个defer任务: EvlDefer_Create(evl, func, arg)
二层结构。
创建一个延迟任务,会将任务的执行函数和执行参数放到结构体中。
启动一个defer任务: EvlDefer_Start(defer)
即,将该任务让如evl的 defer_mgr 队列。
EVL服务拿到时间片会自动执行。
销毁一个defer任务: EvlDefer_Destroy(defer)
USAGE:
Step 1: 创建一个EVL实例。
g_evl = Evl_Create();
Evl_Init(g_evl); // 创建内部队列。
Step 2: 创建线程 启动服务。
pthread_create(&g_thread, NULL, evl_run, (void *)g_evl);
evl_run:启动EVL服务:Evl_Run(evl);
Step 3: 创建其他线程,向服务中 push task。
EvlDefer_t* def = EvlDefer_Create(g_evl, defer_req, transaction);
EvlDefer_Start(def);
Step 4: 任务处理函数 处理和释放 defer 资源。
用户数据:defer->arg
EvlDefer_Destroy(defer);
【代码实现】
evl_def.h
#ifndef _EVL_DEF__H_
#define _EVL_DEF__H_
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include "../list/dlist.h"
typedef struct Evl Evl_t;
typedef struct DeferMgr_s DeferMgr_t;
typedef struct EvlDefer_s EvlDefer_t;
typedef void (*EvlDeferFn_t)(EvlDefer_t* defer);
struct Evl {
DeferMgr_t* defer_mgr;
int exit_req;
};
struct EvlDefer_s //defer node
{
ADT_DLIST_DEF(EvlDefer_s);
EvlDeferFn_t fn;
void* arg;
void* owner;
Evl_t* evl;
};
struct DeferMgr_s {
EvlDefer_t head;
pthread_mutex_t mutex;
};
Evl_t* Evl_Create(void);
int Evl_Init(Evl_t* evl);
void Evl_Exit(Evl_t* evl);
void Evl_Destroy(Evl_t* evl);
int Evl_ProcessMgrs(Evl_t* evl);
void Evl_Run(Evl_t* evl);
EvlDefer_t* EvlDefer_Create(Evl_t* evl, EvlDeferFn_t fn, void* arg);
void EvlDefer_Start(EvlDefer_t* defer);
void EvlDefer_Destroy(EvlDefer_t* defer);
DeferMgr_t* DeferMgr_Create(void);
void DeferMgr_Destroy(DeferMgr_t* obj);
int DeferMgr_Process(DeferMgr_t* obj, int* rv_more);
EvlDefer_t* DeferMgr_DeferCreate(Evl_t* evl, EvlDeferFn_t fn, void* arg);
void DeferMgr_DeferInit(EvlDefer_t* defer, Evl_t* evl, EvlDeferFn_t fn, void* arg);
void DeferMgr_DeferSetCb(EvlDefer_t* defer, EvlDeferFn_t fn, void* arg);
void DeferMgr_DeferStart(DeferMgr_t* obj, EvlDefer_t* defer);
void DeferMgr_DeferStop(DeferMgr_t* obj, EvlDefer_t* defer);
void DeferMgr_DeferDestroy(DeferMgr_t* obj, EvlDefer_t* defer);
#endif
evl.c
#include "evl_defer.h"
Evl_t* Evl_Create(void)
{
Evl_t* evl;
evl = malloc(sizeof *evl);
if (evl == NULL) {
return NULL;
}
memset(evl, 0, sizeof(*evl));
return evl;
}
int Evl_Init(Evl_t* evl)
{
evl->exit_req = 0;
if ((evl->defer_mgr = DeferMgr_Create()) == NULL)
goto fail;
return 0;
fail:
Evl_Destroy(evl);
return -1;
}
void Evl_Destroy(Evl_t* evl)
{
if (evl->defer_mgr != NULL)
DeferMgr_Destroy(evl->defer_mgr);
free(evl);
}
int Evl_ProcessMgrs(Evl_t* evl)
{
int more_work = 0;
int run_count = 0;
if (evl->exit_req)
{
return -1;
}
do {
run_count += DeferMgr_Process(evl->defer_mgr, &more_work);
} while (more_work > 0);
return run_count;
}
void Evl_Run(Evl_t* evl)
{
while (!evl->exit_req)
{
(void)Evl_ProcessMgrs(evl);
}
}
void Evl_Exit(Evl_t* evl)
{
evl->exit_req = 1;
}
EvlDefer_t* EvlDefer_Create(Evl_t* evl, EvlDeferFn_t fn, void* arg)
{
return DeferMgr_DeferCreate(evl, fn, arg);
}
void EvlDefer_Start(EvlDefer_t* defer)
{
DeferMgr_DeferStart(defer->evl->defer_mgr, defer);
}
void EvlDefer_Destroy(EvlDefer_t* defer)
{
DeferMgr_DeferDestroy(defer->evl->defer_mgr, defer);
}
defer.c
//manager
DeferMgr_t* DeferMgr_Create(void)
{
DeferMgr_t* obj = malloc(sizeof *obj);
if (obj) {
ADT_DLIST_HEAD_INIT(&obj->head);
DeferMgr_DeferSetCb(&obj->head, NULL, NULL);
pthread_mutex_init(&obj->mutex, NULL);
}
return obj;
}
void DeferMgr_Destroy(DeferMgr_t* obj)
{
if (obj) {
pthread_mutex_destroy(&obj->mutex);
}
free(obj);
}
//consume one item.
int DeferMgr_Process(DeferMgr_t* obj, int* rv_more)
{
unsigned int count = 0;
pthread_mutex_lock(&obj->mutex);
if (ADT_DLIST_IS_EMPTY(&obj->head)) { /?
pthread_mutex_unlock(&obj->mutex);
return count;
}
EvlDefer_t* entry = ADT_DLIST_NEXT(&obj->head);
ADT_DLIST_UNLINK(entry);
pthread_mutex_unlock(&obj->mutex);
++count;
entry->fn(entry);
const int more = !ADT_DLIST_IS_EMPTY(&obj->head);
if (rv_more != NULL) {
*rv_more = more;
}
return count;
}
EvlDefer_t* DeferMgr_DeferCreate(Evl_t* evl, EvlDeferFn_t fn, void* arg)
{
EvlDefer_t* defer = malloc(sizeof *defer);
if (defer != NULL) {
DeferMgr_DeferInit(defer, evl, fn, arg);
}
return defer;
}
void DeferMgr_DeferInit(EvlDefer_t* defer, Evl_t* evl, EvlDeferFn_t fn, void* arg)
{
defer->evl = evl;
ADT_DLIST_NODE_INIT(defer);
DeferMgr_DeferSetCb(defer, fn, arg);
}
void DeferMgr_DeferSetCb(EvlDefer_t* defer, EvlDeferFn_t fn, void* arg)
{
defer->fn = fn;
defer->arg = arg;
}
void DeferMgr_DeferDestroy(DeferMgr_t* obj, EvlDefer_t* defer)
{
DeferMgr_DeferStop(obj, defer);
free(defer);
}
void DeferMgr_DeferStart(DeferMgr_t* obj, EvlDefer_t* defer)
{
pthread_mutex_lock(&obj->mutex);
if (!ADT_DLIST_IS_LINKED(defer)) {
ADT_DLIST_LINK_PREV(&obj->head, defer);
}
pthread_mutex_unlock(&obj->mutex);
}
void DeferMgr_DeferStop(DeferMgr_t* obj, EvlDefer_t* defer)
{
pthread_mutex_lock(&obj->mutex);
if (ADT_DLIST_IS_LINKED(defer)) {
ADT_DLIST_UNLINK(defer);
}
pthread_mutex_unlock(&obj->mutex);
}
【测试用例】
#include "evl_defer.h"
#include <stdio.h>
#include <unistd.h>
pthread_t g_thread;
Evl_t *g_evl;
void* evl_run(void* arg)
{
Evl_t* evl = arg;
Evl_Run(evl);
}
void defer_req(EvlDefer_t* defer)
{
int *i = defer->arg;
printf("....... %d \n", *i);
free(i);
EvlDefer_Destroy(defer);
}
void test_evl()
{
int i = 0;
while (1) {
int *transaction = malloc(sizeof(int));
*transaction = i++;
printf("defer > %d \n", i);
EvlDefer_t* def = EvlDefer_Create(g_evl, defer_req, transaction);
EvlDefer_Start(def);
sleep(1);
}
}
int main()
{
g_evl = Evl_Create();
Evl_Init(g_evl);
pthread_create(&g_thread, NULL, evl_run, (void *)g_evl);
test_evl();
pthread_join(g_thread, NULL);
while(1) sleep (10);
return 0;
}
【测试结果】
gcc main.c defer.c evl.c -lpthread -g
./a.out
[This is work thread, inserting a task] data:0
[This is proxy thread, handling a task] data:0
[This is work thread, inserting a task] data:1
[This is proxy thread, handling a task] data:1
[This is work thread, inserting a task] data:2
[This is proxy thread, handling a task] data:2
[This is work thread, inserting a task] data:3
[This is proxy thread, handling a task] data:3
[This is work thread, inserting a task] data:4
[This is proxy thread, handling a task] data:4
[This is work thread, inserting a task] data:5
[This is proxy thread, handling a task] data:5