close

C/C++ 的volatile
C/C++中的volatile使用時機?

.不知各位對volatile(揮發性的)這個字陌不陌生? 我相信大家在一些程式或多或少都看
 過這個字眼, 但是究竟要在何種場合用它呢?
.當然一定是有需要, C/C++才會有這個保留字, 否則只是增加programmer的困擾而已
.有2兩個場合(I/O & multithread program), 供各位參考!
.請大家check自己的程式中(尤其是第2個場合), 若有的話請記得加上volatile

1. I/O, 假設有一程式片斷如下

       U8   *pPort;
       U8   i, j, k;
      
       pPort = (U8 *)0x800000;
   
       i = *pPort;     
       j = *pPort;     
       k = *pPort;     

    以上的i, j, k很有可能被compiler最佳化而導致產生
       i = j = k = *pPort;
    的code, 也就是說只從pPort讀取一次, 而產生 i = j = k 的結果, 但是原本的程式的目
    的是要從同一個I/O port讀取3次的值給不同的變數, i, j, k的值很可能不同(例如從此
    I/O port 讀取溫度), 因此i = j = k的結果不是我們所要的

    怎麼辦 => 用volatile, 將
       U8   *pPort;
    改為
       volatile U8   *pPort;

    告訴compiler, pPort變數具有揮發性的特性, 所以與它有關的程式碼請不要作最佳化動作. 因而  
       i = *pPort;     
       j = *pPort;     
       k = *pPort;     
    此三列程式所產生的code, 會真正地從pPort讀取三次, 從而產生正確的結果

2. Global variables in Multithread program
    => 這是在撰寫multithread program時最容易被忽略的一部份
    => 此原因所造成的bug通常相當難解決(因為不穩定)

    假設有以下程式片斷, thread 1 & thread 2共用一個global var: gData
        thread 1:                                thread 2:                               
                                                                                         
            ...                                      ....                                
            int  gData;                              extern int gData;                   
                                                                                         
            while (1)                                int  i, j, k;                       
            {                                                                            
                ....                                 for (i = 0; i < 1000; i++)
                gData = rand();                      {                                   
                .....                                    /* A */
            }                                            j = gData;                      
                                                         ....                            
            ....                                     }                                   

    在thread 2的for loop中, 聰明的compiler看到gData的值, 每次都重新從memory load到register,
    實在沒效率, 因此會產生如下的code(注意,tmp也可以更進一步的用register取代):
       tmp = gData;
       for (i = 0; i < 1000; i++           
       {                                   
           /* A */
           j = tmp;                      
           ....                            
       }                                   
    也就是gData只讀取一次, 這下子問題來了, 說明如下:
    .thread 2在執行for loop到j = gData的前一列(A)的時候(假設此時gData=tmp=5), 被切換到thread 1執行
    .在thread 1的while loop中透過gData = rand(), 對gData做了修改(假設改為1), 再切換回thread 2執行
    .繼續執行 j = gData, 產生j = 5的結果
    .但是正確的結果應該是 j = 1
    怎麼辦 => 也是用volatile,

    在thread 1中, 將
        int  gData;  
    改為
        volatile int  gData;  
   
    在thread 2中, 將
        extern int  gData;  
    改為
        extern volatile int  gData;  

arrow
arrow
    全站熱搜

    freestyler 發表在 痞客邦 留言(19) 人氣()