Design Patterns : Protect Proxy

来源:互联网 发布:淘宝退款什么时候到账 编辑:程序博客网 时间:2024/05/16 06:38

用了一组第三方的API, C风格的接口.

最近发现这组接口非线程安全, 在多线程下执行结果不正确或报错.

接口提供者也不可能将接口改成线程安全(也许是时间不够,或者认为接口提供的没问题),


如果是没问题,你先告诉我接口是非线程安全啊,从头到尾,就告诉我,他的Demo调用这些接口没问题. 奇葩啊.

如果我是接口提供者,有关我提供的接口的各种问题,我都会提供解决方法,你总要让调用者舒服吧? 要是有替代品,谁还用有问题的API呢?

如果是调用者使用你的API出了问题,你总要定位问题,并提出解决方法吧?


如果非要在主线程中调用,因为有些接口执行时间特别的长..., 那UI就卡成一坨, 没法用了.

接口提供者告诉我他的Demo运行正常, 我看到了他的Demo的运行效果,UI卡成一坨, 没发看啊.

他告诉我将他的接口在我的主线程中用,这种馊主意也出,我晕死.

我第一感就是要搞一个代理模式,将事情办了.  他的Demo运行正常的原因就是单线程环境,这很明显.



使用代理模式,可以对第三方的API进行访问控制.

* 是否某个API只执行一次,

* 是否某个API每次执行都加锁

* 在某个API每次执行执行之前或之后,可以做一些额外的附加任务. 这和业务逻辑有关.


原始的第三方API模拟定义

/// @file3rdapi.h/// @brief第三方提供的API, C类型的接口, 非线程安全.///以Lib的形式提供, 我们不可能去改她///API接口集合里面有2种类型的接口///* 装载型, 只支持执行一次.///* 非线程安全型, 执行时必须加锁.///现在模拟成我们自己实现#ifndef __3RD_API_H__#define __3RD_API_H__#define error_code_base1000#define error_code_ok(error_code_base + 1)#define error_code_err(error_code_base + 2)#define error_code_special_err_0(error_code_base + 3)#define error_code_special_err_1(error_code_base + 4)#define error_code_special_err_n(error_code_base + 5)boolfn_api_do_once();intfn_api_no_thread_safe_0();intfn_api_no_thread_safe_1(void* pPame1);intfn_api_no_thread_safe_n(int iParam1);#endif // #ifndef __3RD_API_H__

封装后的代理接口定义, 接口名称在原接口上扩展, 参数相同.

/// @file3rd_api_manager.h/// @brief用保护代理的方法, 实现对第三方接口的访问控制///接口参数同第三方接口, 名字在第三方接口名字上扩展一个后缀#ifndef __3RD_API_MANAGER_H__#define __3RD_API_MANAGER_H__#include "3rd_api.h"boolfn_api_do_once_by_manager();intfn_api_no_thread_safe_0_by_manager();intfn_api_no_thread_safe_1_by_manager(void* pPame1);intfn_api_no_thread_safe_n_by_manager(int iParam1);#endif // #ifndef __3RD_API_MANAGER_H__

在自定义的扩展接口中,用类实现调用原始接口.

/// @file3rd_api_manager.cpp#include "stdafx.h"#include "3rd_api_manager.h"#include "LogicFor3rdApi.h"CLogicFor3rdApig_LogicFor3rdApi;boolfn_api_do_once_by_manager(){return g_LogicFor3rdApi.fn_api_do_once_();}intfn_api_no_thread_safe_0_by_manager(){return g_LogicFor3rdApi.fn_api_no_thread_safe_0_();}intfn_api_no_thread_safe_1_by_manager(void* pPame1){return g_LogicFor3rdApi.fn_api_no_thread_safe_1_(pPame1);}intfn_api_no_thread_safe_n_by_manager(int iParam1){return g_LogicFor3rdApi.fn_api_no_thread_safe_n_(iParam1);}

最终的操作在类中实现, 扩展后的接口只是调用类的接口.

#pragma once#include "stdafx.h"#include "MyLocker.h"using namespace std;class CLogicFor3rdApi{public:CLogicFor3rdApi();virtual ~CLogicFor3rdApi();public:boolfn_api_do_once_();intfn_api_no_thread_safe_0_();intfn_api_no_thread_safe_1_(void* pPame1);intfn_api_no_thread_safe_n_(int iParam1);private:void Lock() { m_Locker.Lock(); }void UnLock() { m_Locker.UnLock(); }private:CMyLockerm_Locker;boolm_b3rdapi_do_once;boolm_b3rdapi_do_once_return_code;};

#include "stdafx.h"#include "LogicFor3rdApi.h"#include "3rd_api.h"CLogicFor3rdApi::CLogicFor3rdApi(){m_b3rdapi_do_once = false;m_b3rdapi_do_once_return_code = false;}CLogicFor3rdApi::~CLogicFor3rdApi(){}boolCLogicFor3rdApi::fn_api_do_once_(){Lock();do{if (m_b3rdapi_do_once){break;}m_b3rdapi_do_once = true;m_b3rdapi_do_once_return_code = fn_api_do_once();} while (0);UnLock();return m_b3rdapi_do_once_return_code;}intCLogicFor3rdApi::fn_api_no_thread_safe_0_(){intiRc = 0;Lock();iRc = fn_api_no_thread_safe_0();UnLock();return iRc;}intCLogicFor3rdApi::fn_api_no_thread_safe_1_(void* pPame1){intiRc = 0;Lock();iRc = ::fn_api_no_thread_safe_1(pPame1);UnLock();return iRc;}intCLogicFor3rdApi::fn_api_no_thread_safe_n_(int iParam1){intiRc = 0;Lock();/// @todo do some task berore call 3rd apiiRc = fn_api_no_thread_safe_n(iParam1);/// @todo do some task after call 3rd apiUnLock();return iRc;}

锁类用临界区实现了一个.

/// @fileMyLocker.h#ifndef __MY_LOCKER_H__#define __MY_LOCKER_H__#include "stdafx.h"class CMyLocker{public:CMyLocker();virtual ~CMyLocker();public:void Lock();void UnLock();private:CRITICAL_SECTION m_locker;};#endif // #ifndef __MY_LOCKER_H__

#include "stdafx.h"#include "MyLocker.h"CMyLocker::CMyLocker(){::InitializeCriticalSection(&m_locker);}CMyLocker::~CMyLocker(){::DeleteCriticalSection(&m_locker);}void CMyLocker::Lock(){::EnterCriticalSection(&m_locker);}void CMyLocker::UnLock(){::LeaveCriticalSection(&m_locker);}

测试程序:

// demoProtectProxy.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "3rd_api_manager.h"int _tmain(int argc, _TCHAR* argv[]){/// now can run the below api many timesfn_api_do_once_by_manager();fn_api_do_once_by_manager();/// now can run the below api on threadfn_api_no_thread_safe_0_by_manager();fn_api_no_thread_safe_1_by_manager(NULL);fn_api_no_thread_safe_n_by_manager(rand());/// prog endgetwchar();/** run result>> fn_api_do_once>> fn_api_no_thread_safe_0>> fn_api_no_thread_safe_1>> fn_api_no_thread_safe_n*/return 0;}




0 0
原创粉丝点击