================================================================================
标题: 对象状态类
作者: 叶飞虎
日期: 2020.01.30
在多线程编程中,但凡对象的操作必须在某种状态下才有效,且对象状态的变化是需要一
定的时钟周期,这时就涉及状态迁移。利用好对象状态可以提高程序的并发能力,同时也能
控制对象状态。
对象状态类的头文件(KYObjState.h)如下:
// =======================================
// Unit : 对象状态类
// Version: 4.0.0.0 (build 2020.01.30)
// Author : Kyee Ye
// Email : kyee_ye(at)126.com
// Copyright (C) Kyee workroom
// =======================================
#ifndef _KYObjState_H_
#define _KYObjState_H_
#include "KYSyncObj.h"
// KYLib 2.0 开始使用 KYLib 命名空间
namespace KYLib
{
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* TKYObjState - 对象状态类 */
class TKYObjState
{
public:
// 执行结果
enum TResult
{orSuccess = 1, // 成功
orFailure = 0, // 失败
orStateInvalid = -1}; // 状态无效
// 状态
enum TState
{osInactive = 0, // 未打开
osClosing = 1, // 正在关闭
osOpening = 2, // 正在打开
osOpened = 3, // 已经打开
osLogouting = 4, // 正在登出
osLogging = 5, // 正在登录
osLogged = 6}; // 已登录
// 回调方法, 注: TDoRet 返回值为 orSuccess 表示成功, 其它值为错误码.
typedef int (TObject::*TDoRet)(void* AParam);
typedef void (TObject::*TDoNone)();
private:
// 不支持对象拷贝
TKYObjState(const TKYObjState& obj) {}
public:
// 构造/析构函数
TKYObjState();
~TKYObjState() { Close(true); }
// 属性
TState State() const { return FState; } // default: osInactive
// 设置 DoOpen, DoClose 和 DoInit 回调方法
bool SetDoOpen(void* AObject, TDoRet ADoOpen, TDoNone ADoClose,
TDoNone ADoInit = NULL);
// 设置 DoLogin 和 DoLogout 回调方法
// 注: 在此两个回调方法中不允许调用 Close 或 SetClosed 方法, 否则会死循环.
bool SetDoLogin(void* AObject, TDoRet ADoLogin, TDoNone ADoLogout);
// 引用计数加/减 1
bool IncRefCountGE(TState AState); // 当 FState >= AState 时引用计数加 1
bool IncRefCount(TState AState); // 当 FState == AState 时引用计数加 1
bool IncRefCount() { return IncRefCountGE(osOpened); }
void DecRefCount() { InterlockedDecrement(&FRefCount); }
// 打开/关闭/设置已关闭(不执行 DoClose 回调)
// 注: AParam 参数用于传入 DoOpen 回调方法
int Open(void* AParam = NULL);
void Close(bool ANeedWait = false);
void SetClosed();
// 登录/登出
// 注: AParam 参数用于传入 DoLogin 回调方法
int Login(void* AParam = NULL);
void Logout();
// 加锁/解锁
void Lock() { FLock.Enter(); }
void Unlock() { FLock.Leave(); }
protected:
private:
// 执行关闭/登出
void DoClose(bool ANeedClose);
void DoLogout() { if (FDoLogout != NULL) (FObjLogin->*FDoLogout)(); }
private:
TKYCritSect FLock; // 状态锁
TState FState; // 状态
int FRefCount; // 状态引用计数
Longword FCloseThreadID; // 当前正在 Close 的线程ID
// 执行打开/关闭的方法
TObject* FObjOpen; // FDoOpen 和 FDoClose 方法的对象
TDoRet FDoOpen; // 执行打开的回调方法
TDoNone FDoClose; // 执行关闭的回调方法
TDoNone FDoInit; // 执行关闭后初始化的回调方法
// 执行登录/加载/登出的方法
TObject* FObjLogin; // FDoLogin和 FDoLogout 方法的对象
TDoRet FDoLogin; // 执行登录的回调方法
TDoNone FDoLogout; // 执行登出的回调方法
};
}
#endif
对象状态类的代码文件(KYObjState.cpp)如下:
// =======================================
// Unit : 对象状态类
// Version: 4.0.0.0 (build 2020.01.30)
// Author : Kyee Ye
// Email : kyee_ye(at)126.com
// Copyright (C) Kyee workroom
// =======================================
#include "KYObjState.h"
// KYLib 2.0 开始使用 KYLib 命名空间
namespace KYLib
{
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* TKYObjState - 对象状态类 */
// ---------------- 静态常量 ----------------
// 成功返回值
static const int _Bool_Ret[2] = {TKYObjState::orStateInvalid, TKYObjState::orSuccess};
// ---------------- 构造函数和析构函数 ----------------
// 构造函数
TKYObjState::TKYObjState()
{
FState = osInactive;
FRefCount = 0;
FCloseThreadID = 0;
FObjOpen = NULL;
FDoOpen = NULL;
FDoClose = NULL;
FDoInit = NULL;
FObjLogin = NULL;
FDoLogin = NULL;
FDoLogout = NULL;
}
// ---------------- 私有函数 ----------------
// 执行关闭
void TKYObjState::DoClose(bool ANeedClose)
{
// 判断是否需要关闭
if (ANeedClose && FDoClose != NULL)
(FObjOpen->*FDoClose)();
// 等待 RefCount == 0
while (FRefCount > 0)
Sleep(8);
// 执行初始化
if (FDoInit != NULL)
(FObjOpen->*FDoInit)();
}
// ---------------- 公有函数 ----------------
// 设置 DoOpen, DoClose 和 DoInit 回调方法
bool TKYObjState::SetDoOpen(void* AObject, TDoRet ADoOpen, TDoNone ADoClose, TDoNone ADoInit)
{
// 初始化
bool result = false;
// 判断 ADoOpen 非空
if (ADoOpen != NULL)
{
Lock();
if (FState == osInactive)
{
FObjOpen = (TObject*)AObject;
FDoOpen = ADoOpen;
FDoClose = ADoClose;
FDoInit = ADoInit;
result = true;
}
Unlock();
}
// 返回结果
return result;
}
// 设置 DoLogin 和 DoLogout 回调方法
// 注: 在此两个回调方法中不允许调用 Close 或 SetClosed 方法, 否则会死循环.
bool TKYObjState::SetDoLogin(void* AObject, TDoRet ADoLogin, TDoNone ADoLogout)
{
// 初始化
bool result = false;
// 判断 ADoLogin 非空
if (ADoLogin != NULL)
{
Lock();
if (FState == osInactive)
{
FObjLogin = (TObject*)AObject;
FDoLogin = ADoLogin;
FDoLogout = ADoLogout;
result = true;
}
Unlock();
}
// 返回结果
return result;
}
// 引用计数加 1, 当 FState >= AState 时引用计数加 1
bool TKYObjState::IncRefCountGE(TState AState)
{
// 初始化
bool result = false;
// 引用计数加 1
InterlockedIncrement(&FRefCount);
if (FState >= AState)
result = true;
else
InterlockedDecrement(&FRefCount);
// 返回结果
return result;
}
// 引用计数加 1, 当 FState == AState 时引用计数加 1
bool TKYObjState::IncRefCount(TState AState)
{
// 初始化
bool result = false;
// 引用计数加 1
InterlockedIncrement(&FRefCount);
if (FState == AState)
result = true;
else
InterlockedDecrement(&FRefCount);
// 返回结果
return result;
}
// 打开
int TKYObjState::Open(void* AParam)
{
// 初始化
Longword lwThread = GetCurrentThreadId();
bool boolNext = false;
int result = orFailure;
// 更改状态
Lock();
if (FState != osInactive)
result = _Bool_Ret[FState >= osOpened];
else if (FDoOpen != NULL)
{
FState = osOpening;
boolNext = true;
FCloseThreadID = lwThread;
}
Unlock();
// 判断是否继续
if (boolNext)
{
// 执行打开, 若成功则添加连接请求
boolNext = false;
result = (FObjOpen->*FDoOpen)(AParam);
// 更改状态
Lock();
if (FState != osOpening)
boolNext = true;
else if (result == orSuccess)
{
FState = osOpened;
FCloseThreadID = 0;
}
else
FState = osInactive;
Unlock();
// 判断是否需要关闭
if (boolNext)
{
// 执行关闭
if (result == orSuccess)
{
DoClose(true);
result = orFailure;
}
// 更改状态(不需要加锁)
FState = osInactive;
}
}
// 返回结果
return result;
}
// 关闭
void TKYObjState::Close(bool ANeedWait)
{
// 检查状态
if (FState == osInactive)
return;
// 初始化
Longword lwThread = GetCurrentThreadId();
bool boolNext = false;
bool boolWait = false;
// 更改状态
Lock();
if (FState >= osOpened)
{
FState = osClosing;
boolNext = true;
FCloseThreadID = lwThread;
}
else if (FState == osOpening)
{
FState = osClosing;
boolWait = ANeedWait && (lwThread != FCloseThreadID);
}
else if (FState == osClosing)
boolWait = ANeedWait && (lwThread != FCloseThreadID);
Unlock();
// 判断是否继续
if (boolNext)
{
// 执行关闭
DoClose(true);
// 更改状态(不需要加锁)
FState = osInactive;
}
else if (boolWait) // 等待 Close 结束
while (FState == osClosing)
Sleep(8);
}
// 设置已关闭(不执行 DoClose 回调)
void TKYObjState::SetClosed()
{
// 初始化
Longword lwThread = GetCurrentThreadId();
bool boolNext = false;
// 更改状态
Lock();
if (FState >= osOpened)
{
FState = osClosing;
boolNext = true;
FCloseThreadID = lwThread;
}
else if (FState == osOpening)
FState = osClosing;
Unlock();
// 判断是否继续
if (boolNext)
{
// 执行关闭
DoClose(false);
// 更改状态(不需要加锁)
FState = osInactive;
}
}
// 登录
int TKYObjState::Login(void* AParam)
{
// 初始化
int result = orFailure;
bool boolNext = false;
// 更改状态
Lock();
if (FState != osOpened)
result = _Bool_Ret[FState >= osLogged];
else if (FDoLogin != NULL)
{
FState = osLogging;
boolNext = true;
InterlockedIncrement(&FRefCount);
}
Unlock();
// 判断是否继续
if (boolNext)
{
// 执行登录
boolNext = false;
result = (FObjLogin->*FDoLogin)(AParam);
// 更改状态
Lock();
if (FState != osLogging)
boolNext = true;
else if (result == orSuccess)
FState = osLogged;
else
FState = osOpened;
Unlock();
// 判断是否需要登出
if (boolNext)
{
// 执行登出
if (result == orSuccess)
{
DoLogout();
result = orFailure;
}
// 更改状态
Lock();
if (FState == osLogouting)
FState = osOpened;
Unlock();
}
// 引用计数减 1
DecRefCount();
}
// 返回结果
return result;
}
// 登出
void TKYObjState::Logout()
{
// 初始化
bool boolNext = false;
// 更改状态
Lock();
if (FState == osLogged)
{
FState = osLogouting;
boolNext = true;
InterlockedIncrement(&FRefCount);
}
else if (FState == osLogging)
FState = osLogouting;
Unlock();
// 判断是否继续
if (boolNext)
{
// 执行登出
DoLogout();
// 更改状态
Lock();
if (FState == osLogouting)
FState = osOpened;
Unlock();
// 引用计数减 1
DecRefCount();
}
}
}
对象状态及示例代码(ObjState.rar)下载如下:
https://pan.baidu.com/s/1eTWTYD0#list/path=%2Fshares%2Fsources%2F_open
================================================================================