线程安全的对象生命期管理(一)

线程安全的定义:

        1、多个线程同时访问时,其表现出正确的行为

        2、无论操作系统如何调度这些线程,无论这些线程的执行顺序如何交织

        3、调用端代码无须额外的同步或其他协调动作。(如在调用函数前后加锁等)

当析构函数遇到多线程:

        C++程序员需要自己管理对象的生命期,这在多线程下显得尤为困难。这里讨论当多个线程同时可以看到一个对象时,对象销毁就有可能出现以下的情形:在即将析构一个对象时,需要知道此刻是否有其他线程在执行该对象的成员函数。

一个线程安全的Counter

class Counter: boost::noncopyable
{
public:
	Counter() :valued(0){}
    int value() const;
	int getAndIncrease();
private:
	int valued;
	mutable MutexLock mutex;
};

int Counter::value()const
{
	MutexLockGuard lock(mutex);
	return valued;
}

int Counter::getAndIncrease()
{
	MutexLockGuard lock(mutex);
	int ret = valued++;
	return ret;
}

        这是一个线程安全的类,但如果动态创建该类的对象并通过指针来访问,那么多线程下对象析构的问题仍然存在,因为内部加的锁仅仅能够保证对象存在情况下执行不会出错,但对象本身总要析构,一析构,锁的作用就不复存在。因为锁本身就是数据成员。

 

对象构造的线程安全:

        唯一的要求是在构造期间不要泄漏this指针,因为对象构造开始到结束要对对象进行修改只能通过this指针实现。即以下几点:

        1、不要在构造函数中注册任何回调

        2、也不要在构造函数中把this传给跨线程的对象

        3、即使在构造函数的最后一行也不行(因为类可能会被继承,所以最后一行后可能还未开始构造派生类的部分,仍然不安全)

反例(使用观察者模式):

        

class Foo:public Observer
{
public:
	Foo(Observable* s)
	{
		s->myregister(this);
	}
	virtual void upodate();
}

正确的方式:

        

class Foo public Observer
{
public:
	Foo();
	virtual void update();

	void observer(Observerable* s)
	{
		s->myregister(this);
	}
};

Foo* pFoo = new Foo;
Observable* s = getSubject();
pFoo->observe(s); //二段式构造,为了避免构造时泄漏this指针的无奈之举。

 

另外,不能同时读写一个class的两个对象,如果类中有锁,多线程下可能发生死锁。

void swap(Counter& a,Counter& b)
{
	MutexLockGuard aLock(a.mutex);
	MutexLockGuard bLock(b.mutex);
}

以上代码看似没问题,但特殊情况下,同时执行swap(a,b)和swap(b,a)就会死锁。所以多线程代码必须考虑到特殊的情况,就和单线程中的边界处理一样,作为一个程序员首要考虑的就是程序的健壮性。

 

 

如何解决我们的第一个问题(如何确定对象还活着,没有被析构)?

       看似简单的解决方案:使用对象池来存储对象(换言之,只构造不析构,完事)。

       说说对象池的缺点:

               对象池的线程安全,应该都知道对象池中的对象用完后会放回对象池中(其实相当于并没有解决析构的本质问题,只是形式上是这样。)

               全局共享数据引发的锁征用(惊群现象?)

               如果共享对象的类型不止一种,使用模板?

       最后,使用对象池的话,如果对象注册了任何非静态成员函数回调,那么必然在某处持有了this指针,这就同样会引发第一个问题。

一个典型的场景:

          

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

class Observerable
{
public:
	void myregister(Observer* x);
	void unregister(Observer* x);
	void notifyObservers(){
		for(Observer* x:observer)
		{
			x->update();
		}
	}

private:
	vector<Observer*> observer;
};

         这里的问题是Observerable如何得知Observer是否还活着?不然x指针就无法使用,第一个想到的是Observer析构时进行解注册,但这里又回到了第一个问题,因为泄漏了this指针。

         

class Observer
{
public:
	void observe(Observerable* s)
	{
		s->myregister(this);
		subject = s;
	}
	virtual ~Observer()
	{
		subject->unregister(this);
	}
	virtual void update() = 0;

	Observerable* subject;
};

           这时候应该想到加锁来解决,但不是最好的解决办法,最好可以有一种方式来帮助我们管理对象,可惜指针和引用方式无法帮助我们提供对象是否存活。所以,使用智能指针是一种好办法,但也有需要注意的问题。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值