一份錯誤的程序的思考和反思,關於多線程編程中的競態問題

直接上代碼 

#include <memory>
#include <iostream>
#include <string.h>
#include <vector>
#include <pthread.h>
using namespace std;

class MutexLock
{
public:
    MutexLock()
    {
        pthread_mutex_init(&_mutex, NULL);
    }

    ~MutexLock()
    {
        pthread_mutex_destroy(&_mutex);
    }

    void lock()
    {
        pthread_mutex_lock(&_mutex);
    }

    void unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    pthread_mutex_t * getMutexLockPtr()
    {
        return &_mutex;
    }
private:
    pthread_mutex_t _mutex;
};

//RAII
class MutexLockGuard
{
public:
    MutexLockGuard(MutexLock & mutex)
            : _mutex(mutex)
    {
        printf("lock\n");
        _mutex.lock();
    }

    ~MutexLockGuard()
    {
        printf("unlock\n");
        _mutex.unlock();
    }

private:
    MutexLock & _mutex;
};

//end of namespace wd

class Observable;

class Observer{
public:
    virtual void update() = 0;
};

class Foo : public Observer
{
public:
    virtual void update()
    {
        printf("%ld:Foo::update() %p\n",pthread_self(), this);
    }
};


class Observable{
public:
    void register_(weak_ptr<Observer> x)
    {
        observers.push_back(x);
    }

    void notifyObservers()
    {
        MutexLockGuard guard(mutex_);
        Iterator it = observers.begin();
        while(it != observers.end())
        {
            shared_ptr<Observer> obj(it->lock());

            if(obj)
            {
                obj->update();
            }else{
                it = observers.erase(it);
            }
            it++;
        }

    }

private:
    mutable MutexLock mutex_;
    vector<weak_ptr<Observer>> observers;
    typedef std::vector<weak_ptr<Observer>>::iterator Iterator;
};

Observable subject;
int sum = 10;

void* threadOne(void* arg)
{
        int count = 0;
        shared_ptr<Foo> foo = make_shared<Foo>();
        weak_ptr<Foo> w(foo);
        subject.register_(w);
        subject.notifyObservers();


}

void* threadTwo(void* arg)
{
    int count = 0;
    shared_ptr<Foo> foo = make_shared<Foo>();
    subject.register_(foo);
    subject.notifyObservers();


}

void* threadThree(void* arg)
{
    int count = 0;
    shared_ptr<Foo> foo = make_shared<Foo>();
//    subject.register_(foo);
//    subject.notifyObservers();
    count++;
}

int main()
{

    pthread_t thread1;
    pthread_t thread2;
    pthread_t thread3;
    pthread_create(&thread1, nullptr,threadOne,nullptr);
    pthread_create(&thread2,nullptr,threadTwo,nullptr);
    pthread_create(&thread3,nullptr,threadThree,nullptr);

    pthread_join(thread1,nullptr);
    pthread_join(thread2,nullptr);
    pthread_join(thread3,nullptr);

}

 

其實可以思考這份代碼,他是哪裏出了問題呢?

 

根本原因是我的線程沒有做時序控制  

threadOne

 

threadTwo

 

threadThree
 

這三個函數可能在另一個線程subject.notifyObservers運行完成之前就結束了,所以observers.erase(it)的時候這個對象已經被釋放掉了,然後再次對他observers.erase可能core dump就是他最好的歸宿了,所以在這種情況下即使加了鎖也並不是安全的 

 

最後我們用一個例子來說明這個問題

 

這是一個不正確的例子:

int main()
{


    vector<weak_ptr<Foo>> observers;
    ;
    {
        shared_ptr<Foo> p(new Foo);
        weak_ptr<Foo> f(p);
        observers.push_back(f);

    }

    std::vector<weak_ptr<Foo>>::iterator Iterator = observers.begin();
    while(Iterator != observers.end())
    {
        shared_ptr<Observer> obj(Iterator->lock());

        if(obj)
        {
            obj->update();
        }else{
            Iterator = observers.erase(Iterator);
        }
        Iterator++;
    }

}

 

出了作用域之後weak_ptr<Foo> f被釋放掉了,出現了

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

 

但是

int main()
{


    vector<weak_ptr<Foo>> observers;
    ;
    {
        shared_ptr<Foo> p(new Foo);
        weak_ptr<Foo> f(p);
        observers.push_back(f);
        std::vector<weak_ptr<Foo>>::iterator Iterator = observers.begin();
        while(Iterator != observers.end())
        {
            shared_ptr<Observer> obj(Iterator->lock());

            if(obj)
            {
                obj->update();
            }else{
                Iterator = observers.erase(Iterator);
            }
            Iterator++;
        }
    }



}

如果這樣的話就不會出現問題,一切安好,因爲沒有走出作用域所以weak_ptr沒有被釋放

/home/zhanglei/ourc/test/cmake-build-debug/test-cpp
140718134794048:Foo::update() 0x55bee3748e70

Process finished with exit code 0

錯誤原因是我不該給Iterator迭代器++,但是還是出現了coredump 我真的很疑惑 原因再這裏

 

最後又查閱了c++ primer 看到了書中關於erase的介紹

 

erase 刪除迭代器p中的指定元素,返回一個指向被刪除元素後的迭代器,如果是末尾則指向off-the-end迭代器

 

所以在這裏如果我們使用了erase 就不要再給迭代器++了

 

最後我們寫一個線程安全的觀察者模式

#include <memory>
#include <iostream>
#include <string.h>
#include <vector>
#include <pthread.h>
using namespace std;

class MutexLock
{
public:
    MutexLock()
    {
        pthread_mutex_init(&_mutex, NULL);
    }

    ~MutexLock()
    {
        pthread_mutex_destroy(&_mutex);
    }

    void lock()
    {
        pthread_mutex_lock(&_mutex);
    }

    void unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    pthread_mutex_t * getMutexLockPtr()
    {
        return &_mutex;
    }
private:
    pthread_mutex_t _mutex;
};

//RAII
class MutexLockGuard
{
public:
    MutexLockGuard(MutexLock & mutex)
            : _mutex(mutex)
    {
        _mutex.lock();
    }

    ~MutexLockGuard()
    {
        _mutex.unlock();
    }

private:
    MutexLock & _mutex;
};

//end of namespace wd

class Observable;

class Observer{
public:
    virtual void update() = 0;
};

class Foo : public Observer
{
public:
    virtual void update()
    {
        printf("%ld:Foo::update() %p\n",pthread_self(), this);
    }
};


class Observable{
public:
    void register_(weak_ptr<Observer> x)
    {
        MutexLockGuard guard(mutex_);
        observers.push_back(x);
    }

    void notifyObservers()
    {
        MutexLockGuard guard(mutex_);
        Iterator it = observers.begin();
        while(it != observers.end())
        {
            shared_ptr<Observer> obj(it->lock());
            if(obj)
            {
                obj->update();
                it++;
            }else{
                it = observers.erase(it);
            }
        }

    }

private:
    mutable MutexLock mutex_;
    vector<weak_ptr<Observer>> observers;
    typedef std::vector<weak_ptr<Observer>>::iterator Iterator;
};

Observable subject;
int sum = 10;

void* threadOne(void* arg)
{
    shared_ptr<Foo> foo = make_shared<Foo>();
    weak_ptr<Foo> w(foo);
    subject.register_(w);
    subject.notifyObservers();


}

void* threadTwo(void* arg)
{
    shared_ptr<Foo> foo = make_shared<Foo>();
    subject.register_(foo);
    subject.notifyObservers();


}

void* threadThree(void* arg)
{
    shared_ptr<Foo> foo = make_shared<Foo>();
    subject.register_(foo);
    subject.notifyObservers();
}

int main()
{

    pthread_t thread1;
    pthread_t thread2;
    pthread_t thread3;
    pthread_create(&thread1, nullptr,threadOne,nullptr);
    pthread_create(&thread2,nullptr,threadTwo,nullptr);
    pthread_create(&thread3,nullptr,threadThree,nullptr);

    pthread_join(thread1,nullptr);
    pthread_join(thread2,nullptr);
    pthread_join(thread3,nullptr);
}

 

注意push_back不是線程安全的,沒有鎖,可能在register_一半的時候切片到另一個線程,然後造成,vector迭代器不完整,所以也是需要加鎖的

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