• C基础 如何让代码只执行一次


    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
    ", ++_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
    ", ++_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

    回头看看, 时间好快呀.  那些个一块补习, 为了玩的伙伴们, 二胎孩子都快上学了. 
    哈哈. 
    咱们一线秃头兵还在为了 穷 而冲锋陷阵 (°ー°〃)
  • 相关阅读:
    负载均衡软件LVS 三种实现模式对比
    论文学习笔记:High-level pattern-based classification via tourist
    论文学习笔记:A Network-Based High Level Data Classification
    第四章--度相关性和社团结构(复杂网络学习笔记)
    第三章--网络基本拓扑性质(复杂网络学习笔记)
    第二章--网络与图(复杂网络学习笔记)
    神经网咯基础-deeplearning.ai【笔记】
    前端Jquery-Ajax跨域请求,并携带cookie
    Django中解决跨域请求问题
    1- 基本概念(复杂网络学习笔记)
  • 原文地址:https://www.cnblogs.com/life2refuel/p/8283435.html
Copyright © 2020-2023  润新知