STL与多线程+写时拷贝

       STL并不是线程安全的,当多个线程同时读取STL时没什么问题。当多个线程中有写STL时则非线程安全,导致其它线程的end()检测或迭代器算术操作无意义,修改操作可能导致STL重新分配内存,原来的迭代器可能失效。要实现多线程安全:可以用锁机制,也可以将写操作推后。

      例子:一个线程输出vector元素,另一个容器不断往vector添加元素。最后出现的结果可能时段错误,也可能运行正常。

#include<iostream>
#include<exception>
#include<pthread.h>
#include<unistd.h>
#include<string>
#include<vector>
#include<boost/noncopyable.hpp>
using namespace std;
using namespace boost;
class Mutex:noncopyable{
    public:
        Mutex(){
            pthread_mutex_init(&mutex,NULL);
        }
        void lock() const{
            pthread_mutex_lock(&mutex);
        }
        void unlock() const{
            pthread_mutex_unlock(&mutex);
        }
        ~Mutex(){
            pthread_mutex_destroy(&mutex);
        }
    private:
        mutable pthread_mutex_t mutex;
};
class MutexLockGuard:noncopyable{
    public:
        explicit MutexLockGuard(Mutex& mutex):mutex_(mutex){//显示构造
            mutex_.lock();
        }
        ~MutexLockGuard(){
            mutex_.unlock();
        }
    private:
        Mutex& mutex_;
};
Mutex mutex;
void* worker1(void* arg){//线程1输出容器元素
    vector<int>* vec=(vector<int>*)arg;
    //sleep(1);
    //MutexLockGuard guard(mutex);
    for(vector<int>::iterator it=(*vec).begin();it!=(*vec).end();it++){
        cout<<*it<<" ";
    }
    cout<<endl;
}
void* worker2(void* arg){//线程2向容器添加元素
    vector<int>* vec=(vector<int>*)arg;
    //MutexLockGuard guard(mutex);
    for(int i=0;i<100;i++){
        (*vec).push_back(i*10);
    }
}
int main(){
    pthread_t pid1,pid2;
    vector<int> vec;
    vec.push_back(-1);
    pthread_create(&pid2,NULL,worker2,&vec);
    pthread_create(&pid1,NULL,worker1,&vec);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    return 0;
}

异常的输出结果部分:

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 段错误 (核心已转储)

采用锁机制(代码注释的部分),正常...


采用写时拷贝技术解决上述问题:

        写容器的线程采用写时拷贝技术(写之前值共享内存,写时先拷贝一份再写到那份拷贝中去)

#include<iostream>
#include<exception>
#include<pthread.h>
#include<unistd.h>
#include<string>
#include<vector>
#include<assert.h>
#include<boost/noncopyable.hpp>
#include<boost/shared_ptr.hpp>
using namespace std;
using namespace boost;
class Mutex:noncopyable{//互斥量的封装
    public:
        Mutex(){
            pthread_mutex_init(&mutex,NULL);
        }
        void lock() const{
            pthread_mutex_lock(&mutex);
        }
        void unlock() const{
            pthread_mutex_unlock(&mutex);
        }
        ~Mutex(){
            pthread_mutex_destroy(&mutex);
        }
    private:
        mutable pthread_mutex_t mutex;
};
class MutexLockGuard:noncopyable{//RAII管理互斥量
    public:
        explicit MutexLockGuard(Mutex& mutex):mutex_(mutex){//显示构造
            mutex_.lock();
        }
        ~MutexLockGuard(){
            mutex_.unlock();
        }
    private:
        Mutex& mutex_;//注意是引用,否则会出错
};
Mutex mutex;
shared_ptr<vector<int> > vec_ptr;
void* worker1(void* arg){//线程1输出容器元素
    int i=0;
    do{
        cout<<"read vector"<<endl;
        {//以下操作限制在一个域中
            shared_ptr<vector<int> > temp_ptr;//作为观察者
            {
                MutexLockGuard guard(mutex);//获取锁
                temp_ptr=vec_ptr;//拷贝shared_ptr对象,被管理对象引用计数加1
                assert(!temp_ptr.unique());
            }
            for(vector<int>::iterator it=temp_ptr->begin();it!=temp_ptr->end();it++){//用观察者shared_ptr作为输出
                cout<<*it<<" ";
            }
            cout<<endl;
        }
        sleep(0.1);
    }while(++i<5);//5次输出操作
}
void* worker2(void* arg){//线程2向容器添加元素
    int i=0;
    do{
        {//限制作用域,退出作用域后临时对象会被析构
            cout<<"write vector"<<endl;
            MutexLockGuard guard(mutex);//获取锁
            if(!vec_ptr.unique()){//若有其它读者,不会有多个写者因为上一句锁已经保证了只有一个写者进入此临界区
                vec_ptr.reset(new vector<int>(*vec_ptr));//写时拷贝,此时若有读者,那么读者读取的元素是旧的那个vector,而新的vector将由写者操作
                cout<<"copy on write"<<endl;
            }
            assert(vec_ptr.unique());
            vec_ptr->push_back(i*100);//写入操作
        }
        sleep(0.1);
    }while(++i<5);
}
int main(){
    pthread_t pid1,pid2;
    vec_ptr.reset(new vector<int>(1,-1));
    pthread_create(&pid1,NULL,worker1,NULL);
    pthread_create(&pid2,NULL,worker2,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值