注意指針修飾符的準確含義

首先從一塊兒多線程無鎖算法的事故提及,如下是一個無鎖棧的實現測試,但在開-O2以上優化的狀況下它卻沒法正常工做:node

#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\n");
    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\n");
        return NULL;
}

void *producer2(void *arg)
{
    printf("producer2\n");
    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\n");
    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\n",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\n",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;,問題解決.優化

總之,對指針修飾符關鍵的一點就是,在*以前,修飾的是指向的目標.而在*以後纔是修飾指針自己.ui

相關文章
相關標籤/搜索