线程安全

之前的一篇中提到了,在访问同一个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优化编译,会导致死循环。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值