atomic 原子自增工程用法案例
案例 1 : 简单用法
atomic_int id; atomic_fetch_add(&id, 1)
atomic_uint id; atomic_fetch_add(&id, 1)
上面两种原子自增简单用法. 这里面有潜在问题. 例如第一段代码中 atomic_fetch_add(&id, 1) 这种 id++
加到最后会溢出需要看业务能否接受了. 第二种死板一点, 也是一样看业务取舍, 例如杂糅一些特殊业务值非常困难.
案例2: skynet 项目中 socket id 设计
//github.com/cloudwu/skynet/blob/master/skynet-src/socket_server.c#L345-L371
static int reserve_id(struct socket_server *ss) { int i; for (i=0;i<MAX_SOCKET;i++) { int id = ATOM_FINC(&(ss->alloc_id))+1; if (id < 0) { id = ATOM_FAND(&(ss->alloc_id), 0x7fffffff) & 0x7fffffff; } struct socket *s = &ss->slot[HASH_ID(id)]; int type_invalid = ATOM_LOAD(&s->type); if (type_invalid == SOCKET_TYPE_INVALID) { if (ATOM_CAS(&s->type, type_invalid, SOCKET_TYPE_RESERVE)) { s->id = id; s->protocol = PROTOCOL_UNKNOWN; // socket_server_udp_connect may inc s->udpconncting directly (from other thread, before new_fd), // so reset it to 0 here rather than in new_fd. ATOM_INIT(&s->udpconnecting, 0); s->fd = -1; return id; } else { // retry --i; } } } return -1; }
其中 ATOM_FINC, ATOM_FAND 设计如下
//github.com/cloudwu/skynet/blob/master/skynet-src/atomic.h
#else #include <stdatomic.h> #define ATOM_INT atomic_int #define ATOM_POINTER atomic_uintptr_t #define ATOM_SIZET atomic_size_t #define ATOM_ULONG atomic_ulong #define ATOM_INIT(ref, v) atomic_init(ref, v) #define ATOM_LOAD(ptr) atomic_load(ptr) #define ATOM_STORE(ptr, v) atomic_store(ptr, v) #define ATOM_CAS(ptr, oval, nval) atomic_compare_exchange_weak(ptr, &(oval), nval) #define ATOM_CAS_POINTER(ptr, oval, nval) atomic_compare_exchange_weak(ptr, &(oval), nval) #define ATOM_FINC(ptr) atomic_fetch_add(ptr, 1) #define ATOM_FDEC(ptr) atomic_fetch_sub(ptr, 1) #define ATOM_FADD(ptr,n) atomic_fetch_add(ptr, n) #define ATOM_FSUB(ptr,n) atomic_fetch_sub(ptr, n) #define ATOM_FAND(ptr,n) atomic_fetch_and(ptr, n) #endif
他的思路是, 通过 atomic_fetch_and 来避免 atomic_fetch_add 溢出问题. 但也存在一个问题, 就是 atomic_fetch_and 存在返回相同值情况,
所以返回的 id 会重复. 这里他借助 atomic_compare_exchange_weak + 业务判断 让重复 id 再次重试.
案例3: structc 中 id 设计
//github.com/wangzhione/structc/blob/master/modular/test/timer.c.h#L28-L56
// timer_list 链表对象管理器 struct timer_list { atomic_int id; // 当前 timer node id atomic_flag lock; // 自旋锁 volatile bool status; // true is thread loop, false is stop struct timer_node * list; // timer list list }; // 定时器管理单例对象 static struct timer_list timer = { .id = 1, .lock = ATOMIC_FLAG_INIT }; // get atomic int 1 -> INT_MAX -> 1 static inline int timer_list_id() { // 0 -> INT_MAX -> INT_MIN -> 0 int id = atomic_fetch_add(&timer.id, 1) + 1; if (id < 0) { // INT_MAX + 1 -> INT_MIN // 0x7F FF FF FF + 1 -> 0x80 00 00 00 // INT_MIN & INT_MAX => 0x80 00 00 00 & 0x7F FF FF FF => 0x00 00 00 00 // id = atomic_fetch_and(&timer.id, INT_MAX) & INT_MAX; // Multiple operations atomic_fetch_and can ensure timer.id >= 0 atomic_fetch_and(&timer.id, INT_MAX); // again can ensure id >= 1 id = atomic_fetch_add(&timer.id, 1) + 1; } return id; }
这个设计把 id 分为三段, -1, 0, 还有 > 0 . timer_list_id 函数返回 >= 1 id. 这里利用二次 atomic_fetch_add 原子自增同时继续保持不重复性.
后记:
技巧是为想法和需求服务, 欢迎补充更多原子操作用法.