線程安全

之前的一篇中提到了,在訪問同一個i對象時,可能會有訪問結果跟預期不一致的問題。

今天正好看到了一些線程對內存訪問加鎖的函數,於是上網蒐集了一下,做了個整理。

參考資料:

http://www.cnblogs.com/FrankTan/archive/2010/12/11/1903377.html

http://blog.itmem.com/?p=1286

http://pic.dhe.ibm.com/infocenter/comphelp/v121v141/index.jsp?topic=%2Fcom.ibm.xlc121.aix.doc%2Fcompiler_ref%2Fbif_gcc_atomic_synchronize.html

http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

總得來說,內存加鎖的方式有以下幾種

memory barrier有幾種類型:
    acquire barrier : 不允許將barrier之後的內存讀取指令移到barrier之前(linux kernel中的wmb())。
    release barrier : 不允許將barrier之前的內存讀取指令移到barrier之後 (linux kernel中的rmb())。
    full barrier    : 以上兩種barrier的合集(linux kernel中的mb())。

gcc從4.1.2提供了__sync_*系列的built-in函數,用於提供加減和邏輯運算的原子操作。

其聲明如下:

複製代碼
type __sync_fetch_and_add (type *ptr, type value, ...)type __sync_fetch_and_sub (type *ptr, type value, ...)type __sync_fetch_and_or (type *ptr, type value, ...)type __sync_fetch_and_and (type *ptr, type value, ...)type __sync_fetch_and_xor (type *ptr, type value, ...)type __sync_fetch_and_nand (type *ptr, type value, ...)type __sync_add_and_fetch (type *ptr, type value, ...)type __sync_sub_and_fetch (type *ptr, type value, ...)type __sync_or_and_fetch (type *ptr, type value, ...)type __sync_and_and_fetch (type *ptr, type value, ...)type __sync_xor_and_fetch (type *ptr, type value, ...)type __sync_nand_and_fetch (type *ptr, type value, ...)
複製代碼

這兩組函數的區別在於第一組返回更新前的值,第二組返回更新後的值。

source_code:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

static int count = 0;

void *test_func(void *arg)
{
    int i=0;
    for(i=0;i<20000;++i){
         __sync_fetch_and_add(&count,1);
    
        // count++;     
        
    }   
    return NULL;
}
int main(int argc, const char *argv[])
{
    pthread_t id[20];
    int i = 0;
    for(i=0;i<20;++i){
        pthread_create(&id[i],NULL,test_func,NULL);
    }   
    for(i=0;i<20;++i){
        pthread_join(id[i],NULL);
    }
    printf("%d\n",count);
   
    return 0;
}

如果不用代碼中的代碼,而用註釋的代碼來增加count,那麼結果將不會是20*20000,而是一些隨機的值

bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...
type __sync_val_compare_and_swap (type 
*ptr, type oldval type newval, ...)


這兩個函數提供原子的比較和交換,如果*ptr == oldval,就將newval寫入*ptr,
第一個函數在相等並寫入的情況下返回true.
第二個函數在返回操作之前的值。


_sync_synchronize (...)

發出一個full barrier.

這個例子比較複雜了,

#include <iostream>
#include <cassert>
#include <pthread.h>
#include <stdio.h>


using namespace std;
template<typename T>
class PipeQueue
{
public:
    PipeQueue() {
        head = new Item();
        head->next = NULL;
        tail = head;
    }
    ~PipeQueue() {
        assert(head->next == NULL);
        delete head;
    }
    void Push(const T& data) {
            Item* it = new Item();
            it->next = NULL;
            tail->data = data;
            __sync_synchronize(); // important
            tail->next = it;
            tail = it;
        }
    bool Pop(T& ret) {
            Item* tmp = NULL;
            __sync_synchronize(); // important
            if (head->next != NULL) {
                tmp = head;
                head = head->next;
                ret = tmp->data;
                delete tmp;
                return true;
            } else {
               return false;
            }
        }
private:
    struct Item {
        T data;
        Item* next;
    };
private:
    Item* head;
    Item* tail;
};
static void* thread_writer(void* param)
{
    PipeQueue<int>* queue = static_cast<PipeQueue<int>*>(param);
    for (int i = 0; i < 35000; i++) {
        queue->Push(i);
    }
    return NULL;
}
static void* thread_reader(void* param)
{
    PipeQueue<int>* queue = static_cast<PipeQueue<int>*>(param);
    int i = 0;
    int tmp;
    while (true) {
        if (queue->Pop(tmp)) {
            if (i != tmp) {
                cout<<"data error"<<endl;
                break;
            }
            i++;
            if (i > 30000) {
                break;
            }
        }
    }
    return NULL;
}
static void test()
{
    PipeQueue<int> queue;
    pthread_t writer, reader;
    pthread_create(&reader, NULL, thread_reader, &queue);
    pthread_create(&writer, NULL, thread_writer, &queue);
    pthread_join(reader, NULL);
    pthread_join(writer, NULL);
    int tmp;
    while (queue.Pop(tmp)) {
    }
    cout<<" test2 completed... "<<endl;
}
int main(int argc, char **argv)

    test();
    getchar();
    return 0;
}

如果把那兩行syn加鎖代碼註釋掉,並且用O3優化編譯,會導致死循環。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章