【轉載】C/C++中的volatile使用時機?

(原作者)http://freestyler.pixnet.net/blog/post/23872864-c-c%2B%2B%E4%B8%AD%E7%9A%84volatile%E4%BD%BF%E7%94%A8%E6%99%82%E6%A9%9F%3F
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;  

留言

這個網誌中的熱門文章

為 Line-in 設定音量

Firefox: 設定滑鼠滾輪捲動行數