class U
{
public:
int Guid; //用户唯一ID
}
std::vector<class U> g_UserList;
std::vector<class U*> g_pUserList;
std::vector<shared_ptr<class U> > g_spUserList;
CMutex Mutex;
void GuidLock(int Guid) 可以根据 Guid 的值来使用不同实例的用户锁(实现方式不详述)
一、实体
class U GetUser()
{
Mutex.lock();
U u = g_UserList[0];
Mutex.unlock();
return u;
}
U u = GetUser();
GuidLock( u.Guid );
u.working();
GuidUnlock( u.Guid );
使用实体容器时全局锁的粒度可以达到最小, 在容器取出对象的代码之间上全局锁, 取出后再把实例独有的锁上锁
优点: 全局锁粒度小
缺点: 增加构造与析构花费, 需额外的实例对象锁
二、原指针
class U* GetUser()
{
Mutex.lock();
U u* = g_pUserList[0];
Mutex.lock();
return u;
}
U *u = GetUser();
GuidLock( u->Guid );
u->working();
GuidUnlock( u->Guid );
在使用原始指针后这样的代码变得不安全, 因为其它线程可能在 GetUser后 与 GuidLock 之前 delete User 造成崩溃
解决方案: 扩大Mutex粒度
class U* GetUser()
{
U u* = g_pUserList[0];
return u;
}
Mutex.lock();
U* pU = GetUser();
pU->working();
Mutex.unlock();
优点: 没有额外的构造与析构, 也不用对每个用户创建一个用户锁
缺点: 全局锁粒度非常大
三、引用计数指针
shared_ptr<class U> GetUser()
{
Mutex.lock();
shared_ptr<class U> spU = g_spUserList[0];
Mutex.unlock();
return spU;
}
shared_ptr<class U> spU = GetUser();
GuidLock( spU->Guid );
spU->working();
GuidUnlock( spU->Guid );
优点: 接近原始指针的性能, 只有引用计数的构造与析构消耗
缺点: 需额外的实例对象锁
总结:
就这样看来, 在 class U 的构造与析构远大于shared_ptr时, 无疑用 shared_ptr 效率最佳, 虽然原始指针不用为每个用户增加一个对象锁, 但只用一个全局锁的逻辑概念下除了在同一个线程并且使用自旋锁的情况下, 其它线程都没办法取得其它用户对象, 性能极为低效.
但引用计数指针还存在一个继承问题, 当 class U 是继承其它 class 的情况下
class U : public class Object
{
};
原始指针可以进行这样的操作
U *pU = GetUser();
Object *pO = pU;
但智能指针却不能这样
shared_ptr<U> spU = GetUser();
shared_ptr<Object> spO = spU; //shared_ptr<U> 不是继承 shared_ptr<Object>的子类
这样的情况其实可以通过 Object *pO = &(*spU); 临时解决, 但并不代表没有其它问题, 当把 pO 放入其它逻辑进行处理后该如何访问如下函数?
const char* GetUserName( shared_ptr<U> &pUser );
当然, 也可以写一个 const char* GetUserName( U *pU ); 来解决这个问题, 然而, 如果 GetUserName 并不是单纯把拿出其中一个成员, 而是要把 pU push_back 进一个待处理的事件列表 std::list<shared_ptr<U> > waitList 又该如何解决这个问题? 除非把 push_back 拉到上层逻辑, 封装性尽失.
虽然说实例比shared_ptr多了构造和析构的消耗, 但这种模式的确可以解决这样的问题
const char* GetUserName( Object &obj );
U u = GetUser();
O &o = u;
GetUserName( o );
[ 本帖最后由 木头人 于 2010-2-10 02:50 编辑 ] |