场景
- 在前面的文章说过C++实现synchronized方式的对象锁[1]可以实现
Java
的synchronized
易用特性。这个第一版的例子并没有说明如何删除pthread_mutex_t
对象,随着加锁的对象增加会出现内存泄漏的情况,怎么解决?
说明
-
在
C++11
提供了可复制的共享的指针std::shared_ptr
, 利用这个指针我们可以判断mutex
有没有被其他线程使用,从而释放mutex
。 -
改进版的例子使用
C++11
的<mutex>
库来创建互斥量,不使用pthread
, 读者可以自己写一个类来封装pthread_mutex_t
来替换<mutex>
。 -
gBASMapMutex
全局锁用来获取对象锁,因为全局锁保护的代码区执行很快,所以它的时间可以忽略。主要还是业务逻辑的对象锁。
void BASLock::GLock(void* object)
{
gBASMapMutex.lock();
shared_ptr<void> lock;
if(gLockObject.find(object) != gLockObject.end()){
lock = gLockObject[object];
}else{
auto spMutex = make_shared<std::mutex>();
gLockObject[object] = spMutex;
lock = spMutex;
}
gBASMapMutex.unlock();
auto one = (std::mutex*)lock.get();
one->lock();
}
- 锁代码块可以利用
C++
的对象生命周期特性, 像以下方式使用,只要方法结束就会解锁。或者利用更加弹性的BASLock::GLock
来加锁。
static void doSynchronizedWork()
{
BASLock lock(&mKey);
...
}
例子
bas_lock.h
#ifndef __BAS_LOCK_H
#define __BAS_LOCK_H
class BASLock
{
public:
BASLock(void* object);
~BASLock();
static void GLock(void* object);
static void GUnLock(void* object);
public:
static int MutexSize();
private:
void* lock_object_;
};
#endif
bas_lock.cpp
#include "bas_lock.h"
#include <map>
#include <memory>
#include <mutex>
#include <iostream>
using namespace std;
static std::map<void*,shared_ptr<std::mutex>> gLockObject;
static std::mutex gBASMapMutex;
int BASLock::MutexSize()
{
return gLockObject.size();
}
BASLock::BASLock(void* object)
{
lock_object_ = object;
GLock(object);
}
BASLock::~BASLock()
{
GUnLock(lock_object_);
}
void BASLock::GLock(void* object)
{
gBASMapMutex.lock();
shared_ptr<void> lock;
if(gLockObject.find(object) != gLockObject.end()){
lock = gLockObject[object];
}else{
auto spMutex = make_shared<std::mutex>();
gLockObject[object] = spMutex;
lock = spMutex;
}
gBASMapMutex.unlock();
auto one = (std::mutex*)lock.get();
one->lock();
}
void BASLock::GUnLock(void* object)
{
gBASMapMutex.lock();
auto lock = gLockObject[object];
if (lock.use_count() == 2) // 当前计数为2个时: 局部变量lock 和 gLockObject里的两个。
gLockObject.erase(gLockObject.find(object));
gBASMapMutex.unlock();
auto one = (std::mutex*)lock.get();
one->unlock();
}
test-synchronized
#include <iostream>
#include "bas_lock.h"
#include <thread>
using namespace std;
static int64_t mKey = 0;
static int64_t mValue = 0;
static const int gTextThreadNum = 10;
static void doSynchronizedWork()
{
cout << "thread id: " << std::this_thread::get_id() << endl;
BASLock lock(&mKey);
for (auto i = 0; i < 1000000; i++)
mKey++;
cout << "mKey: " << mKey << endl;
}
static void doNoSynchronizedWork()
{
cout << "thread id: " << std::this_thread::get_id() << endl;
for (auto i = 0; i < 1000000; i++)
mValue++;
cout << "mValue: " << mValue << endl;
}
int main(int argc, char const *argv[]){
cout << "hello" << endl;
std::thread threads[gTextThreadNum];
// synchronized
cout << "---- synchronized ----" << endl;
for (auto i = 0; i < gTextThreadNum; ++i)
threads[i] = move(std::thread(doSynchronizedWork));
for (auto i = 0; i < gTextThreadNum; ++i)
threads[i].join();
cout << "mutex size: " << BASLock::MutexSize() << endl;
// no synchronized
cout << "---- no synchronized ----" << endl;
for (auto i = 0; i < gTextThreadNum; ++i)
threads[i] = move(std::thread(doNoSynchronizedWork));
for (auto i = 0; i < gTextThreadNum; ++i)
threads[i].join();
system("pause");
return 0;
}
输出
hello
---- synchronized ----
thread id: 10084thread id: 21516
thread id: 24956
thread id: 7344thread id: 8120
thread id: 3292
thread id: 23244thread id:
thread id: 10452
2148
thread id: 10812
mKey: 1000000
mKey: 2000000
mKey: 3000000
mKey: 4000000
mKey: 5000000
mKey: 6000000
mKey: 7000000
mKey: 8000000
mKey: 9000000
mKey: 10000000
mutex size: 0
---- no synchronized ----
thread id: 3632
thread id: 18928thread id: thread id: 24032thread id: 22196thread id: 14028
thread id: 8180
12936
thread id: 18080
thread id: 21480
mValue: 586629
mValue: 625623
thread id: 22556
mValue: 1300269
mValue: 1530590
mValue: 1562043
mValue: 1615115
mValue: 1810808
mValue: 1924919
mValue: 2055872
mValue: 2090888