zoukankan      html  css  js  c++  java
  • 注意指针修饰符的准确含义

    首先从一起多线程无锁算法的事故说起,以下是一个无锁栈的实现测试,但在开-O2以上优化的情况下它却无法正常工作:

    #include "lf_stack.h"
    #include "kn_list.h"
    #include "kn_time.h"
    #include "kn_thread.h"
    #include "kn_atomic.h"
    
    typedef struct lockfree_stack
    {
    	volatile kn_list_node *head;
    }lockfree_stack,*lockfree_stack_t;
    
    static  void lfstack_push(lockfree_stack_t ls,kn_list_node *n)
    {
    	for( ; ;){	
    		kn_list_node *lhead = ls->head;
    		n->next = lhead;
    		if(COMPARE_AND_SWAP(&ls->head,lhead,n))//if head unchange,set n to be the new head
    			break;
    	}
    }
    
    static  kn_list_node* lfstack_pop(lockfree_stack_t ls)
    {
    	for( ; ;){	
    		kn_list_node *lhead = ls->head;
    		if(!lhead) return NULL;
    		kn_list_node *next = lhead->next;				
    		if(COMPARE_AND_SWAP(&ls->head,lhead,next))
    		{
    			lhead->next = NULL;
    			return lhead;
    		}
    	}
    }
    
    
    volatile int count = 0;
    atomic_32_t c1 = 0;
    atomic_32_t c2 = 0;
    
    struct element{
    	kn_list_node node;
    	int value;
    };
    
    struct element *element_pool1;
    struct element *element_pool2;
    
    lockfree_stack lf_stack;
    
    void *producer1(void *arg)
    {
    	printf("producer1
    ");
    	int i;
    	while(1){
    		for(i = 0; i < 10000000; ++i){
    			struct element *ele =  &element_pool1[i];
    			ATOMIC_INCREASE(&c1);
    			lfstack_push(&lf_stack,(kn_list_node*)ele);
    		}
    		while(c1 > 0){
    			FENCE();
    			kn_sleepms(0);
    		}
    	}
    	printf("producer1 end
    ");
        	return NULL;
    }
    
    void *producer2(void *arg)
    {
    	printf("producer2
    ");
    	int i;
    	while(1){
    		for(i = 0; i < 10000000; ++i){
    			struct element *ele =  &element_pool2[i];
    			ATOMIC_INCREASE(&c2);	
    			lfstack_push(&lf_stack,(kn_list_node*)ele);
    
    		}
    		while(c2 > 0){
    			FENCE();
    			kn_sleepms(0);
    		}
    	}
        return NULL;
    }
    
    
    void *consumer(void *arg)
    {
    	printf("consumer
    ");
    	volatile struct element *ele;
    	uint32_t tick = 0;
    	while(1){
    		if((ele = (struct element*)lfstack_pop(&lf_stack))){
    			if(count == 0){
    				 tick = kn_systemms();
    			}
    			if(++count  == 5000000) {
    				uint32_t now = kn_systemms();
                				uint32_t elapse = (uint32_t)(now-tick);
    				printf("pop %d/ms
    ",count/elapse*1000);
    				tick = now;
    				count = 0;				
    			}
    			if(ele->value == 1)
    				ATOMIC_DECREASE(&c1);
    			else if(ele->value == 2)
    				ATOMIC_DECREASE(&c2);
    			else
    				printf("%d
    ",ele->value);	
    		}
    	}
        return NULL;
    }
    
    int main(){
    	element_pool1 = calloc(10000000,sizeof(*element_pool1));
    	element_pool2 = calloc(10000000,sizeof(*element_pool2));
    	int i;
    	for(i = 0; i < 10000000; ++i) element_pool1[i].value = 1;
    	for(i = 0; i < 10000000; ++i) element_pool2[i].value = 2;		
    	lf_stack.head = NULL;
    	kn_thread_t t1 = kn_create_thread(THREAD_JOINABLE);
    	kn_thread_t t2 = kn_create_thread(THREAD_JOINABLE);	
    	kn_thread_t t3 = kn_create_thread(THREAD_JOINABLE);	
    	kn_thread_start_run(t1,producer1,NULL);
    	kn_thread_start_run(t2,producer2,NULL);	
    	kn_thread_start_run(t3,consumer,NULL);		
     	getchar();
    	return 0;
    }
    

    表现就是consumer执行一定次数的pop之后死活也无法再弹出元素.不知道各位看官看出问题在哪没有.

    当问题再次出现以后,我用调试器上去中断,consumer线程,断点正好在这行if(!lhead) return NULL;,lhead为NULL,我回到上一层栈查看实际上lockfree_stack.head字段并不是空,当我想查看lhead的地址时,显示无法查看寄存器地址.

    那么问题就明确了,编译器把lhead存放在了寄存器,导致无法发现head实际已经被改变.那么问题来了,我明明将head标记为volatile的呀.

    可是再仔细看看,volatile kn_list_node *head;修饰符在指针之前,意思是指向的是volatile变量,而我实际要的是,一个指针它本身是volatile的.ok,做相应的调整后kn_list_node * volatile head;,问题解决.

    总之,对指针修饰符关键的一点就是,在*之前,修饰的是指向的目标.而在*之后才是修饰指针本身.

  • 相关阅读:
    嵌入式编程中使用 do{...} while(0) 的解释
    ESP32学习笔记(一) 环境搭建与下载
    预告:准备开个坑,集中学习一下esp32模块
    【信号与系统】多项式化简方法
    nginx二级域名代理
    nginx配置ssl证书
    springBoot使用阿里云的证书
    vue-cli3项目开启less支持并引入短链接
    一键安装系列
    centos7增加swap
  • 原文地址:https://www.cnblogs.com/sniperHW/p/4190088.html
Copyright © 2011-2022 走看看