这将是我关于Linux Epoll服务器开发系列文章中的第一篇,关于线程同步的操作,大部分是原创,少部分是借鉴。
命名风格近似于Google C++ Coding Style,但根据自己的习惯做了调整。
这些代码将用于构建一个高性能的Socket服务器,贴出来的目的是与大家交流,如果有幸能为朋友们所用,请一定要记得给我反馈,方便我改进,帮助我进步,谢谢!
我的邮件地址:miscab@gmail.com
由于跨平台的代码面目可憎,暂时我只维持一个Linux的版本。
要编译以下代码请加入一些基本类型定义:
typedef signed char int8;
typedef short int16;
typedef int int32;
typedef long long int64;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;
并加入以下的头文件定义:
#include <pthread.h>
#include <semaphore.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/time.h>
1. 参考了网上的代码,融入了Google的编码风格,为每个对象定义判定初始化是否成功的bool成员变量isInitialized。
同时抽象出Lock和Unlock接口,让各类mutex和semaphore去实现具体的Lock和Unlock操作。
class SyncObject
{
public:
SyncObject( ) { isInitialized = false; }
virtual ~SyncObject( ) { }
virtual int32 Lock ( ) = 0;
virtual int32 Unlock( ) = 0;
bool IsInitialized() { return isInitialized; }
protected:
bool isInitialized;
};
class ThreadMutex : public SyncObject
{
public:
ThreadMutex()
{
if(0 != pthread_mutexattr_init(&mutexAttribute))
{
isInitialized = false;
return;
}
if(0 != pthread_mutex_init(&mutexHandle, &mutexAttribute))
{
pthread_mutexattr_destroy(&mutexAttribute);
isInitialized = false;
return;
}
isInitialized = true;
}
virtual ~ThreadMutex()
{
if(isInitialized)
{
pthread_mutexattr_destroy(&mutexAttribute);
pthread_mutex_destroy(&mutexHandle);
isInitialized = false;
}
}
pthread_mutex_t* getMutex() { return &mutexHandle; }
virtual int32 Lock() { return pthread_mutex_lock(&mutexHandle); }
virtual int32 Unlock() { return pthread_mutex_unlock(&mutexHandle); }
virtual int32 TryLock() { return pthread_mutex_trylock(&mutexHandle); }
protected:
pthread_mutex_t mutexHandle;
pthread_mutexattr_t mutexAttribute;
};
2. 读写锁的封装,Lock()等于LockRead()。
class ThreadReadWriteMutex : public SyncObject
{
public:
ThreadReadWriteMutex( )
{
if(0 != pthread_rwlockattr_init(&readWriteMutexHandleAttr))
{
isInitialized = false;
return;
}
if(0 != pthread_rwlock_init(&readWriteMutexHandle, &readWriteMutexHandleAttr))
{
pthread_rwlockattr_destroy(&readWriteMutexHandleAttr);
isInitialized = false;
return;
}
isInitialized = true;
}
virtual ~ThreadReadWriteMutex()
{
if(isInitialized)
{
pthread_rwlockattr_destroy(&readWriteMutexHandleAttr);
pthread_rwlock_destroy(&readWriteMutexHandle);
isInitialized = false;
}
}
pthread_rwlock_t* getReadWriteMutexHandle() { return &readWriteMutexHandle; }
virtual int32 LockRead() { return pthread_rwlock_rdlock(&readWriteMutexHandle); }
virtual int32 LockWrite() { return pthread_rwlock_wrlock(&readWriteMutexHandle); }
virtual int32 Unlock() { return pthread_rwlock_unlock(&readWriteMutexHandle); }
virtual int32 TryLock() { return pthread_rwlock_tryrdlock(&readWriteMutexHandle); }
virtual int32 Lock() { return pthread_rwlock_rdlock(&readWriteMutexHandle); } //default Lock is set to LockRead
protected:
pthread_rwlock_t readWriteMutexHandle;
pthread_rwlockattr_t readWriteMutexHandleAttr;
};
3. 参考网上的Condition封装代码,增加了WaitTimeout接口,这样封装下来Condition的功能就与Windows平台的WaitForSingleObject非常类似了。
大家肯定都有感觉,WaitForSingleObject用起来那是相当顺手,呵呵
class Condition : public SyncObject
{
public:
Condition();
virtual ~Condition() ;
void Wait();
bool WaitTimeout(int ms);
void Signal() ;
void SignalAll() ;
protected:
virtual int32 Lock();
virtual int32 Unlock();
private:
int activeWaiters;
bool isSendSignal;
pthread_cond_t conditionHandle;
ThreadMutex mutexObject;
};
Condition::Condition()
{
isSendSignal = false;
activeWaiters = 0;
pthread_condattr_t condattrDetails;
memset(&condattrDetails, 0, sizeof(condattrDetails));
if(0 != pthread_condattr_init(&condattrDetails))
{
isInitialized = false;
return;
}
if(0 != pthread_cond_init(&conditionHandle, &condattrDetails))
{
isInitialized = false;
return;
}
if(0 != pthread_condattr_destroy(&condattrDetails))
{
isInitialized = false;
return;
}
isInitialized = true;
}
Condition::~Condition()
{
uint32 retryCount = 0;
while ((EBUSY == pthread_cond_destroy(&conditionHandle)) && (retryCount <= 10))
{
retryCount++;
SignalAll();
}
isSendSignal = false;
activeWaiters = 0;
isInitialized = false;
}
void Condition::Wait(void)
{
Lock();
++activeWaiters;
while (!isSendSignal)
{
pthread_cond_wait(&conditionHandle, mutexObject.getMutex());
}
--activeWaiters;
if (0 == activeWaiters)
{
isSendSignal = false;
}
Unlock();
}
bool Condition::WaitTimeout(int ms)
{
struct timespec timeout;
struct timeval tp;
bool isTimeOut = false;
gettimeofday(&tp, NULL);
timeout.tv_sec = (ms / 1000) + tp.tv_sec;
timeout.tv_nsec = ((ms % 1000) * 1000000) + (tp.tv_usec * 1000);
while (timeout.tv_nsec >= 1000000000)
{
timeout.tv_nsec -= 1000000000;
++timeout.tv_sec;
}
Lock();
++activeWaiters;
int32 result = 0;
while (!isSendSignal)
{
if (ETIMEDOUT == (result = pthread_cond_timedwait(&conditionHandle, mutexObject.getMutex(), &timeout)))
{
isTimeOut = true;
break;
}
}
--activeWaiters;
if ((0 == result) &&
(0 == activeWaiters))
{
isSendSignal = false;
}
Unlock();
return !isTimeOut; //if timeout return false, else true
}
void Condition::Signal(void)
{
Lock();
if (!isSendSignal)
{
pthread_cond_signal(&conditionHandle);
isSendSignal = true;
}
Unlock();
}
void Condition::SignalAll(void)
{
Lock();
pthread_cond_broadcast(&conditionHandle);
isSendSignal = true;
Unlock();
}
int32 Condition::Lock(void)
{
return mutexObject.Lock();
}
int32 Condition::Unlock(void)
{
return mutexObject.Unlock();
}
4. 自旋锁的封装,有的时候自旋锁比Mutex更有效率
class SpinLock : public SyncObject
{
public:
SpinLock();
virtual ~SpinLock();
virtual int32 Lock();
virtual int32 Unlock();
virtual int32 TryLock();
private:
pthread_spinlock_t spinLockHandle;
};
SpinLock::SpinLock()
{
if(0 != pthread_spin_init(&spinLockHandle, 0))
{
isInitialized = false;
}
else
{
isInitialized = true;
}
}
SpinLock::~SpinLock()
{
if(isInitialized)
{
pthread_spin_destroy(&spinLockHandle);
isInitialized = false;
}
}
int32 SpinLock::Lock()
{
return pthread_spin_lock(&spinLockHandle);
}
int32 SpinLock::Unlock()
{
return pthread_spin_unlock(&spinLockHandle);
}
int32 SpinLock::TryLock()
{
return pthread_spin_trylock(&spinLockHandle);
}
5. 无命名信号量的封装
class Semaphore : public SyncObject
{
public:
Semaphore(uint32 initialCount = 0);
virtual ~Semaphore();
public:
virtual int32 Lock();
virtual int32 Unlock();
int32 TryLock(uint32 milliseconds);
int32 Peek();
private:
sem_t semaphoreHandle;
};
Semaphore::Semaphore(uint32 initialCount)
{
if(0 != sem_init(&semaphoreHandle, 0, initialCount))
{
isInitialized = false;
}
else
{
isInitialized = true;
}
}
Semaphore::~Semaphore()
{
if(isInitialized)
{
if(0 != sem_destroy(&semaphoreHandle))
{
//todo
}
isInitialized = false;
}
}
int32 Semaphore::Lock()
{
return sem_wait(&semaphoreHandle);
}
int32 Semaphore::TryLock(uint32 milliseconds)
{
const struct timespec timeOut = {0, milliseconds * 1000 * 1000 };
return sem_timedwait(&semaphoreHandle, &timeOut);
}
int32 Semaphore::Unlock()
{
return sem_post(&semaphoreHandle);
}
int32 Semaphore::Peek()
{
int32 semaphoreValue;
if(0 != sem_getvalue(&semaphoreHandle, &semaphoreValue))
{
return -1;
}
return semaphoreValue;
}
6. 还有一些类似于Windows平台InterlockedIncrement之类的封装函数
/*
* if(*lock == old)
* {
* *lock = set;
* }
* else
* {
* old = *lock;
* }
*/
static inline uint32 AtomicCompareAndSet(
uint32 *lock,
uint32 old,
uint32 set)
{
uint8 res;
__asm__ volatile (
"lock;"
"cmpxchgl %3, %1;"
"sete %0;"
: "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");
return res;
}
static inline int32 AtomicAdd(uint32 *value, int32 add)
{
__asm__ volatile (
"lock;"
"xaddl %0, %1;"
:"+r" (add) : "m" (*value) : "cc", "memory");
return add;
}
static inline int32 AtomicIncrement(uint32 *value)
{
return AtomicAdd(value, 1);
}
static inline int32 AtomicDecrement(uint32 *value)
{
return AtomicAdd(value, -1);
}
也差不多了,我写代码用的也就是这些,剩下的还有命名信号量用起来挺舒服的,就像Windows的NamedEvent,用于进程或线程的同步。
有时间把命名信号量封装出来。