一份错误的程序的思考和反思,关于多线程编程中的竞态问题

直接上代码 

#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就是他最好的归宿了,所以在这种情况下即使加了锁也并不是安全的 

扫描二维码关注公众号,回复: 12275369 查看本文章

最后我们用一个例子来说明这个问题

这是一个不正确的例子:

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迭代器不完整,所以也是需要加锁的

猜你喜欢

转载自blog.csdn.net/qq_32783703/article/details/105301601