C基础 如何让代码只执行一次
2018-01-14 16:33
267 查看
1.0 最简单, 最高效的方式
C 代码运行起点 main 就是个大单例函数. 如果把函数注册在其里面, 那么一定很可以 :)
// 某个库需要初始化的函数 void log_init(void) { ... ... } int main(int argc, char * argv[]) { ... ... extern void log_init(void); log_init() ... ... return 0; }
是不是, 很轻松的完成了初始化工作. 不妨赠送一个好用的宏, 用于处理这类事情
// // EXTERN_RUN - 简单的声明, 并立即使用的宏 // ftest : 需要执行的函数名称 // ... : 可变参数, 保留 // #define EXTERN_RUN(ftest, ...) \ do { \ extern void ftest(); \ ftest (__VA_ARGS__); \ } while(0)
用起来更简单, 可以插在代码的任何一处
EXTERN_RUN(log_run);
2.0 多线程模式, 如何搞起呢
继续看下面例子 once.c
#include <stdio.h> #include <pthread.h> static void _once(void) { static long _cnt; printf("_once _cnt = %ld\n", ++_cnt); } // // pthread_once 感受 // int main(int argc, char * argv[]) { pthread_once_t once = PTHREAD_ONCE_INIT; puts("pthread_once 感受开始 ... "); pthread_once(&once, _once); pthread_once(&once, _once); puts("pthread_once 感受结束 ... "); return 0; }
gcc -g -Wall -o once.out once.c -lpthread
最终运行结果, 也是如我们所料那样
pthread_once 实际开发中多用于初始化线程私有变量. 其内部实现加锁的. 不妨问个小问题, 如果需要你去实现 pthread_once 你会怎么分析呢 ? 这个问题好解答也不好解答. 核心亮点在于 pthread_once 运行的函数实体崩溃了. 多线程之间如何避免死锁. 不妨参照下面 winds 上面 pthread_once 一位大佬的实现:
#include "pthread.h" #include "implement.h" /* [i_a] simple wrapper ensures correct calling convention for all */ static void PTW32_CDECL ptw32_mcs_lock_cleanup(void *args) { ptw32_mcs_local_node_t *node = (ptw32_mcs_local_node_t *)args; ptw32_mcs_lock_release(node); } int pthread_once (pthread_once_t * once_control, void (PTW32_CDECL *init_routine) (void)) { if (once_control == NULL || init_routine == NULL) { return EINVAL; } if ((PTW32_INTERLOCKED_LONG)PTW32_FALSE == (PTW32_INTERLOCKED_LONG)PTW32_INTERLOCKED_EXCHANGE_ADD_LONG((PTW32_INTERLOCKED_LONGPTR)&once_control->done, (PTW32_INTERLOCKED_LONG)0)) /* MBR fence */ { ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire((ptw32_mcs_lock_t *)&once_control->lock, &node); if (!once_control->done) { #if defined(PTW32_CONFIG_MSVC7) #pragma inline_depth(0) #endif pthread_cleanup_push(ptw32_mcs_lock_cleanup, &node); (*init_routine)(); pthread_cleanup_pop(0); #if defined(PTW32_CONFIG_MSVC7) #pragma inline_depth() #endif once_control->done = PTW32_TRUE; } ptw32_mcs_lock_release(&node); } return 0; } /* pthread_once */
核心是通过 pthread_cleanup_push 和 pthread_cleanup_pop 解决崩溃死锁问题. 当然还有一种思路, 可以解决上面问题. 不妨往下看.
3.0 跳过锁问题, 尝试原子操作
先举个小例子
#include <stdio.h> static void _once(void) { static long _cnt; printf("_once _cnt = %ld\n", ++_cnt); } // // run once 感受 // int main(int argc, char * argv[]) { puts("run once 感受开始 ... "); { static int _done; if (__sync_bool_compare_and_swap(&_done, 0, 1)) { _once(); } if (__sync_bool_compare_and_swap(&_done, 0, 1)) { _once(); } } puts("run once 感受结束 ... "); return 0; }
运行展示:
这里通过 GCC 提供的 原子交换 __sync_bool_compare_and_swap 解决的. 不妨继续赠送的封装宏, 来完成上面操作
#define ONCE_RUN(code) { \ static int _done; \ if (!_done) { \ if (__sync_bool_compare_and_swap(&_done, 0, 1)) { \ code \ } \ } \ }
因为是原子操作, 没有锁那么重, 自然出了问题也不会引起死锁问题. 当然有人说 pthread, __sync_xxx 都是和 GCC 绑定的, 那么 CL 能不能使用了. 当然也是可以的.
pthread 跨平台 - https://github.com/wangzhione/structc/blob/master/structc/system/thread.h
atomic 跨平台 - https://github.com/wangzhione/atomic/blob/master/atomic/atom.h
通过上面基础封装库支持, 用 C 写系统应用相关代码还是很好搞的.
把酒满上, 还能再写几行代码
有问题, 是肯定的. 欢迎指正, 更新是共同提高的过程.
老鼠爱大米 - http://music.163.com/m/song?id=308789&userid=16529894
回头看看, 时间好快呀. 那些个一块补习, 为了玩的伙伴们, 二胎孩子都快上学了. 哈哈. 咱们一线秃头兵还在为了 穷 而冲锋陷阵 (°ー°〃)
相关文章推荐
- java语言基础|如何在main方法前执行其他代码
- 基础才是重中之重~泛型类的静态构造方法可不是只执行一次呀
- Python基础学习代码之执行环境
- C#如何计算代码执行时间
- 如何在Oracle中一次执行多条sql语句【ado.net】
- 如何在后台代码中执行原生sql?
- 如何在遗留代码基础上开发
- 如何用C#动态编译、执行代码
- java基础-用代码证明,在try中写了return,后面又写了finally,是先执行return还是先执行fianlly?
- 如何在Java项目中执行python代码
- 数字货币开发的底层技术如何实现执行智能合约代码
- C# Javascript引擎,如何在C#中执行现有的Javacript代码?
- C#基础知识复习1代码规范-执行流程(c#)-面向对象-引用命名空间-封装-继承-访问修饰符-虚方法-静态成员-多态-抽象类等
- 在VB.NET中如何检验可执行代码
- PHP内核探索 —— 解释器的执行过程:引擎是如何执行PHP代码的
- Javascript基础——关于JavaScript代码的执行顺序
- 程序员如何才能提高自己?通过一次重构代码讲解自己的感受【有代码比较】
- crontab中如何实现每隔多少天执行一次脚本
- 倍福TwinCAT(贝福Beckhoff)基础教程 松下驱动器如何执行绝对值清零
- VC++如何计算一段代码的执行时间