线程相关的单例模式(C\C++)

最近开发有这样的需求,

我需要一个静态类,我可以在线程中的任何地方调用它的public函数已完成对应的功能;

这个静态类会调用我初始化给它的一个指针,这个指针是与线程一一对应的;

准确来说这种模式应该叫多例模式,它是单例模式和工厂模式的一个变式。下面说一下,我的实现思路。

(一)实例指针

如果是单例模式,会有一个指针或者静态变量来存储这个静态变量,而这里多例,则需要使用一个Map来存储,Map的key是当前线程的句柄,Map定义如下:

typedef map<DWORD, CRelatedThreadMultiton*> ThreadMap;

(二)获取指针

与单例模式相同,构造是私有的,通过静态的接口来获得实例。而与其有所差异的地方在于,如果已经存在于map,我需要从map中拿到对应的instance,而如果不存在,则需要在new之后,将其存放于map中。由于是线程相关的,也就是说一个线程中只会有一个instance,所以它本质上与单线程的单例模式是类似的,不会存在多线程的危险。

下面是获得单例指针的代码:

	// 获得指定的单例
	CRelatedThreadMultiton *pInstance = NULL;
	if (InstanceExisted(dwThreadId))
	{
		pInstance = m_ThreadIdMap[dwThreadId];
	}
	else
	{
		pInstance = new CRelatedThreadMultiton;
		m_ThreadIdMap[dwThreadId] = pInstance;
	}

这里判断是否存在,需要通过遍历map表来实现,实现如下:

BOOL CRelatedThreadMultiton::InstanceExisted( DWORD _dwThreadId )
{
	ThreadMap::iterator itor = m_ThreadIdMap.begin();
	while (itor!=m_ThreadIdMap.end())
	{
		if (itor->first == _dwThreadId)
		{
			return TRUE;
		}
		itor++;
	}

	return FALSE;
}

(三)instance的释放

我认为instance有两个释放时机,一个是我在获得一个新的之前,我可以检查map表中是否有无效的项,如果有,则擦除之。另一个是在析构的时候需要清空map表,删除指针。

删除无效指针的实现如下:

// 删除map中已经失效的指针
BOOL CRelatedThreadMultiton::RemoveInvalidInstance()
{
	ThreadMap::iterator itor = m_ThreadIdMap.begin();
	while (itor!=m_ThreadIdMap.end())
	{
		if (!ThreadExisted(itor->first))
		{
			//delete itor->second;
			itor = m_ThreadIdMap.erase(itor);
		}
		else
			itor++;
	}

	return TRUE;
}

这里需要用到判断一个遍历当前进程的线程的方法,可以通过快照的方式遍历,代码不难,只是需要熟悉几个常用的API,可以如下实现:

// 遍历当前进程的所有线程,判断当前进程是否存在
BOOL CRelatedThreadMultiton::ThreadExisted(DWORD _dwThreadId)
{
	BOOL bRet = FALSE;
	HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
	THREADENTRY32 threadEntry32; 

	// 创建当前进程的快照
	hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
	if( hThreadSnap == INVALID_HANDLE_VALUE )
		return FALSE;

	threadEntry32.dwSize = sizeof(THREADENTRY32 ); 

	// 获得快照的第一个线程
	if( !Thread32First( hThreadSnap, &threadEntry32 ) )
	{
		CloseHandle( hThreadSnap );     // Must clean up the snapshot object!
		return FALSE;
	}

	// 依次遍历所有线程,检查是否存在该线程
	do
	{
		if (threadEntry32.th32ThreadID == _dwThreadId)
		{
			bRet = TRUE;
			break;
		}
	} while( Thread32Next(hThreadSnap, &threadEntry32 ) ); 

	CloseHandle( hThreadSnap );		// 关闭快照

	return bRet;
}
</pre><span style="white-space:pre">		</span>(四)析构</p><p><span style="white-space:pre">		</span>其实,本质上来说,这个线程相关的多例模式就是一个单例模式,可以完全按照单例模式自动析构的方法进行析构。</p><p><span style="white-space:pre">		</span>定义一个内部类,声明其对应的静态成员变量。</p><p><pre name="code" class="cpp">	class CGarbo			// 用于在析构函数中释放各个Instance句柄
	{
	public:
		~CGarbo()
		{
			RemoveInvalidInstance();
		}
	};
	static CGarbo garbo;

需要注意的是,静态的类成员变量,定义和声明是需要分开的,类内声明,类外定义,不多说,下面是map和garbo成员的定义与初始化:

// 为静态成员变量定义
ThreadMap CRelatedThreadMultiton::m_ThreadIdMap;
CRelatedThreadMultiton::CGarbo CRelatedThreadMultiton::garbo;

(五)初始化线程数据与使用线程数据

为了更方便的测试,我们定义一个类,用于模拟线程对应的数据结构,

该测试类如下:

typedef struct _testStruct
{
	int a;
	int b;
	int c;
}testStruct;

然后提供一个PrintC接口用于对外输出

public:
	BOOL PrintC();
BOOL CRelatedThreadMultiton::PrintC()
{
	printf("c:%d\n", m_pTest->c);

	return TRUE;
}

最后写两个线程,用于测试,我们的结果如何:

// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "Multiton.h"

#include<iostream>
#include <process.h>

using namespace std;

//DWORD WINAPI SubThread1(LPVOID lpParam)
unsigned _stdcall SubThread1(void* param)
{
	testStruct test;
	test.c = 1;

	CRelatedThreadMultiton *pInstance = NULL;
	pInstance = CRelatedThreadMultiton::GetRTMultiton();
	pInstance->Init((PVOID *)&test);

	while(TRUE)
	{
		pInstance->PrintC();

		static int j =0;
		j++;
		if (j>10)
		{
			break;
		}
		Sleep(500);
	}

	return 0;
}

unsigned _stdcall SubThread2(void* param)
{
	testStruct test;
	test.c = 2;

	CRelatedThreadMultiton *pInstance = NULL;
	pInstance = CRelatedThreadMultiton::GetRTMultiton();
	pInstance->Init((PVOID *)&test);

	while(TRUE)
	{
		pInstance->PrintC();

		static int i =0;
		i++;
		if (i>10)
		{
			break;
		}
		Sleep(1000);
	}
	return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
	DWORD hProcess = GetCurrentProcessId();

	HANDLE hThread1;
	HANDLE hThread2;

	unsigned int uThreadID1;
	unsigned int uThreadID2;

	hThread1 = (HANDLE)_beginthreadex(NULL, 0, SubThread1, NULL, NULL, &uThreadID1);
	hThread2 = (HANDLE)_beginthreadex(NULL, 0, SubThread2, NULL, NULL, &uThreadID2);

	::WaitForSingleObject(hThread1, INFINITE);
	::WaitForSingleObject(hThread2, INFINITE);

	_endthreadex(uThreadID1);
	_endthreadex(uThreadID2);

	return 0;
}

最后,放一下打印的结果:

ok,现在已经实现了最初希望的目标,如果有任何建议和问题,欢迎大家斧正,谢谢!~

最近发现自己的博客竟然和自己做事一般麻乱,甚是烦躁,趁着端午节假期期间,准备做个小的调整。另,需要学的东西真的好多,就拿设计模式来说,稍微有些编程经验的,一两个小时也能看懂其目的和实现技术,更专家一些,甚至可以自己再独立实现,但是,工作中慢慢用来,才应了陆游的一句诗词“纸上得来终觉浅,须知此事要躬行。”
被认为是最简单的一个单例模式,在面对多线程的时候,都需要有番考量才敢用之。

贴上工程的0分下载地址:

http://download.csdn.net/detail/fukainankai/7425211

线程相关的单例模式(C\C++),布布扣,bubuko.com

时间: 2024-10-17 07:52:52

线程相关的单例模式(C\C++)的相关文章

ndroid网络(4):HttpClient必经之路----使用线程安全的单例模式HttpClient,及HttpClient和Application的融合

上文简 单介绍了HttpClient和Tomcat服务器的交互,主角是HttpClient,然后它跟服务器交互有两种方式即get和post.所以这个 HttpClient就类似于电脑上用的浏览器.当我打开多个网页的时候,并不需要开一个网页就开一个浏览器,而是一个浏览器上面开了好几个网页.对应于 HttpClient,即无需连接一次就new一个HttpClient.一般,我们希望一个应用里就一个HttpClient就ok了,就像我们的手机 或PC,没人会呼呼的装好几个浏览器.本文即解决此问题,代码

Android网络(4):HttpClient必经之路----使用线程安全的单例模式HttpClient,及HttpClient和Application的融合

上文简单介绍了HttpClient和Tomcat服务器的交互,主角是HttpClient,然后它跟服务器交互有两种方式即get和post.所以这个HttpClient就类似于电脑上用的浏览器.当我打开多个网页的时候,并不需要开一个网页就开一个浏览器,而是一个浏览器上面开了好几个网页.对应于HttpClient,即无需连接一次就new一个HttpClient.一般,我们希望一个应用里就一个HttpClient就ok了,就像我们的手机或PC,没人会呼呼的装好几个浏览器.本文即解决此问题,代码可以直接

进程 &amp; 线程相关知识

不管Java,C++都有进程.线程相关的内容.在这里统一整理吧. Python的线程,其实是伪线程,不能真正的并发.下面也有讲. 线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈). 多个线程共享内存. 参考了这篇文章:http://www.cnblogs.com/qiaoconglovelife/p/5319779.html 进程与PCB 进程:进程是程序的一次执行过程,是系统进行资源分配和调度的一个独立单位. 进程实体(进程映像):由程序段.相关数

线程安全的单例模式的几种实现

单例模式是一种常见的设计模式:Java Singleton 模式就为我们提供了这样实现的可能.使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数, 有利于Java垃圾回收(garbage collection). 单例模式也是一种比较常见的设计模式,它到底能带给我们什么好处呢?其实无非是三个方面的作用: 1.控制资源的使用,通过线程同步来控制资源的并发访问: 2.控制实例产生的数量,达到节约资源的目的. 3.作为通信媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让

java笔记--关于Object类与线程相关的方法

关于Object类中的线程方法 Object类是所有Java类的 父类,在该类中定义了三个与线程操作有关的方法,使得所有的Java类在创建之后就支持多线程 这三个方法是:notify(),notifyAll(),wait(),这几个方法都是用来控制线程的运行状态的. 方法列表如下: notify() : 唤醒在此对象监视器上等待的单个线程 notifyAll() : 唤醒在此对象监视器上等待的所有线程 wait() : 在其他线程时调用此对象的notify()或者notifyAll()方法前,导

Android之线程安全的单例模式,Adapter注意事项之引用传值

线程安全的单例模式单位模式一般写法如下: public static FestivalLab mInstance; private FestivalLab() { } public static FestivalLab getInstance() { if (mInstance == null) { mInstance = new FestivalLab(); } return mInstance; } 这样写不是线程安全的,因为如果两个线程都进入到这里,会实例化两次.那么如何优化呢? 线程安全

PyQt5学习笔记15----PyQt 线程相关类

QThread是我们将要详细介绍的第一个类.它也是 Qt 线程类中最核心的底层类.由于 PyQt 的跨平台特性,QThread要隐藏掉所有平台相关的代码. 正如前面所说,要使用QThread开始一个线程,我们可以创建它的一个子类,然后覆盖其QThread.run()函数: class Thread(QThread): def __init__(self): super(Thread,self).__init__() def run(self): pass #线程相关代码 然后我们这样新建一个新的

iOS开发Swift篇(02) NSThread线程相关简单说明

iOS开发Swift篇(02) NSThread线程相关简单说明 一 说明 1)关于多线程部分的理论知识和OC实现,在之前的博文中已经写明,所以这里不再说明. 2)该文仅仅简单讲解NSThread在swift语境中的一些使用和注意点,别他. 3)本文涉及代码可以从https://github.com/HanGangAndHanMeimei/Code地址获得. 二 NSThread的基本使用和创建 1)基本用法(主线程|当前线程) 1 //1.获得执行该方法的当前线程 2 let currentT

APUE: 线程相关库函数

线程有时称为轻权进程. 进程的所有信息对该进程的所有线程都是共享的. 每个线程有一个线程ID,线程ID只在它所属的进程环境中有效. 线程从进程继承的东西: 进程ID 地址空间 浮点环境 信号屏蔽字(不包括未决的信号集) 线程间共享的东西: 进程指令 大部分数据 信号处理程序和信号处理 当前工作目录 用户ID和组ID 线程间独立的东西: 线程ID 寄存器集合 栈 errno 信号掩码 优先级 所有线程函数返回类型为int的成功返回0,失败返回错误码,不设置errno. 线程编程需要链接库: -lp