线程池实现.docx

上传人:b****6 文档编号:7492029 上传时间:2023-01-24 格式:DOCX 页数:41 大小:36.69KB
下载 相关 举报
线程池实现.docx_第1页
第1页 / 共41页
线程池实现.docx_第2页
第2页 / 共41页
线程池实现.docx_第3页
第3页 / 共41页
线程池实现.docx_第4页
第4页 / 共41页
线程池实现.docx_第5页
第5页 / 共41页
点击查看更多>>
下载资源
资源描述

线程池实现.docx

《线程池实现.docx》由会员分享,可在线阅读,更多相关《线程池实现.docx(41页珍藏版)》请在冰豆网上搜索。

线程池实现.docx

线程池实现

Afxpool.exe是一个自解压缩的可执行文件,其中包含演示为基于MFC的ISAPI扩展创建线程池的方法的一个MicrosoftVisualC++6.0项目所需的文件。

本文解释在ISAPI扩展内创建工作线程池的步骤。

通常,当ISAPI扩展需要大量处理时间来完成一个请求时,需要创建单独的控制线程来处理该过程。

有关其他信息,请参见以下Microsoft知识库文章:

185518如何使用MFCISAPI扩展实现工作线程

但是,如果为每个新请求都创建一个新的控制线程,则会对服务器的性能产生很大影响。

因此,最好是创建一个可以“回收”线程的工作线程池。

这种方法的作用是,将在处理新请求的开销中除去创建新线程的开销。

ISAPI扩展在第一次初始化时将发生创建线程的开销,但是对于每个请求,它将只从线程池中请求可用的工作线程。

回到顶端

 

可以从Microsoft下载中心下载以下文件:

Afxpool.exe

有关如何下载Microsoft支持文件的其他信息,请单击下面的文章编号,以查看Microsoft知识库中相应的文章:

119591如何从联机服务获取Microsoft支持文件

Microsoft已对此文件进行了病毒扫描。

Microsoft使用的是该文件发布时可以获得的最新病毒检测软件。

该文件存储在安全性得到增强的服务器上,以防止在XX的情况下对其进行更改。

文件名大小

---------------------------------------------------------

ExtThreadPool.aps18,436

ExtThreadPool.clw420

ExtThreadPool.cpp6,184

ExtThreadPool.def173

ExtThreadPool.dsp4,332

ExtThreadPool.dsw549

ExtThreadPool.h1,560

ExtThreadPool.ncb66,560

ExtThreadPool.opt54,784

ExtThreadPool.plg723

ExtThreadPool.rc3,242

ExtThreadPool.rc2405

Resource.h322

StdAfx.cpp215

StdAfx.h669

ThreadPool.cpp3,130

ThreadPool.h863

线程池使用WindowsNT和Windows2000操作系统系列下的可用完成端口。

这种线程池的基本原理是创建一个单独的完成端口,使所有的工作线程在此完成端口上等待,当新请求进入时,将该请求发送至此完成端口。

完成端口机制可确保只有一个控制线程获得这个新请求的通知。

如果不使用完成端口而使用其他同步对象(如信号灯),也可以创建并管理线程池。

但是,使用完成端口时,操作系统会以最有效的方式调度和释放线程(即“后进先出”)。

在典型的ISAPI扩展中,“ISAPIMFC向导”将创建两个虚函数:

GetExtensionVersion和TerminateExtension。

完成端口的初始化是在GetExtensionVersion中通过调用CreateIoCompletionPort来完成的。

请注意,使用此函数的最后一个参数

,您可以指定可同时从线程池中运行的线程数。

最有效的方法是使运行的线程数与CPU数相同。

最后一个参数如果是0,则表示允许操作系统选取与CPU数相匹配的线程数。

BOOLCExtThreadPoolExtension:

:

GetExtensionVersion(

HSE_VERSION_INFO*pVer)

{

//Calldefaultimplementationforinitialization

CHttpServer:

:

GetExtensionVersion(pVer);

//Loaddescriptionstring

TCHARsz[HSE_MAX_EXT_DLL_NAME_LEN+1];

ISAPIVERIFY(:

:

LoadString(AfxGetResourceHandle(),

IDS_SERVER,sz,HSE_MAX_EXT_DLL_NAME_LEN));

_tcscpy(pVer->lpszExtensionDesc,sz);

m_phThreads=newHANDLE[THREAD_COUNT];

m_nThreadCount=THREAD_COUNT;

m_hIoPort=CreateIoCompletionPort((HANDLE)INVALID_HANDLE_VALUE,

NULL,0,0);

//NOTE:

IfaCWinAppobjectisnotdeclare,thiscallshouldbe

//changedtoCreateThread()instead.

for(longn=0;n<m_nThreadCount;n++)

{

CWinThread*pWinThread=AfxBeginThread(

(AFX_THREADPROC)ThreadProc,m_hIoPort);

m_phThreads[n]=pWinThread->m_hThread;

}

returnTRUE;

}

此处,在CExtThreadPoolExtension类中定义了三个变量:

m_nThreadCount、m_hIoPort和m_phThreads。

m_nThreadCount是长整型,用于定义在线程池中创建的线程数(在ThreadPool.h文件中将该值硬编码为THREAD_COUNT变量)。

m_hIoPort是单独的完成端口句柄,您应将其发送给所有工作线程进行等待。

m_phThreads是来自工作线程的线程句柄数组。

应当注意的是,本示例是在共享库中使用MFC构建的。

如果使用向导将MFCISAPI扩展创建为静态链接库,则CWinApp的实例将不可用。

因此,AfxBeginThread()调用应当替换为CreateThread()。

虽然“MFCISAPI向导”创建了GetExtensionVersion和TerminateExtension方法,但是还需要手动替代CHttpServer的HttpExtensionProc()方法。

这可以通过在CExtThreadPoolExtension声明中添加该方法来完成:

classCExtThreadPoolExtension:

publicCHttpServer

{

public:

CExtThreadPoolExtension();

~CExtThreadPoolExtension();

//Overrides

//ClassWizardgeneratedvirtualfunctionoverrides

//NOTE-theClassWizardwilladdandremovemember

//functionshere.

//DONOTEDITwhatyouseeintheseblocksofgeneratedcode!

//{{AFX_VIRTUAL(CExtThreadPoolExtension)

public:

virtualBOOLGetExtensionVersion(HSE_VERSION_INFO*pVer);

virtualDWORDHttpExtensionProc(EXTENSION_CONTROL_BLOCK*pECB);

//}}AFX_VIRTUAL

virtualBOOLTerminateExtension(DWORDdwFlags);

//TODO:

Addhandlersforyourcommandshere.

//Forexample:

voidDefault(CHttpServerContext*pCtxt);

DECLARE_PARSE_MAP()

//{{AFX_MSG(CExtThreadPoolExtension)

//}}AFX_MSG

protected:

HANDLE*m_phThreads;

HANDLEm_hIoPort;

longm_nThreadCount;

};

这是必须的,因为必须替代CHttpServer:

:

HttpExtensionProc()返回的值,以使IIS知道您仍在处理此请求,而不回收EXTENSION_CONTROL_BLOCK。

替代HttpExtensionProc的代码相当简单:

DWORDCExtThreadPoolExtension:

:

HttpExtensionProc(

EXTENSION_CONTROL_BLOCK*pECB)

{

//Theresultofthebaseclass's(CHttpServer)HttpExtensionProc()

//mustbechecked.Ifit'ssuccessful,youwillneedtoreturna

//HSE_STATUS_PENDING.ThisissothatIISwillnotshutdownthe

//connectionandnotdestroytheECB.

//Iftheresultofthebaseclassisnotsuccessful,youshould

//simplypropagatetheerrormessageuptoIIS.

DWORDdwResult=CHttpServer:

:

HttpExtensionProc(pECB);

if(dwResult==HSE_STATUS_SUCCESS)

returnHSE_STATUS_PENDING;

else

returndwResult;

}

只须检查从CHttpServer:

:

HttpExtensionProc()中返回的调用,如果成功,则返回pending。

否则,仅传送出现的错误。

在Default()实现(或在消息映射中MFCISAPI扩展调用的任一例程)中,提取EXTENSION_CONTROL_BLOCK(请注意,这与CHttpServerContext对象不同;不能将CHttpServerContext对象传递给工作线程,因为该对象仅存在于CHttpServer:

:

HttpExtensionProc()作用域内),并将其投递到在GetExtensionVersion()中创建的完成端口:

voidCExtThreadPoolExtension:

:

Default(CHttpServerContext*pCtxt)

{

if(!

PostQueuedCompletionStatus(m_hIoPort,

(DWORD)pCtxt->m_pECB,0,NULL))

{

charszBuffer[4096]={0};

sprintf(szBuffer,"<H1>Errorpostingtocompletionport!

Win32Error=%i</H1>",GetLastError());

DWORDdwBufLen=strlen(szBuffer);

pCtxt->m_pECB->WriteClient(pECB->ConnID,szBuffer,

&dwBufLen,0);

}

}

如果完成端口投递失败,则需要生成一条错误消息,并将其重新报告给客户端。

请注意,要输出这一错误,请从ECB(而不是MFC包装程序)中直接使用WriteClient回调。

此外,本示例还演示将各种参数传递给工作线程的方法。

除了Default()外,还有ProcessName()和ProcessSample()。

ProcessName()采用三个字符串参数:

Firstname、Lastname和Middlename。

ProcessSample采用两个参数:

Data和String。

Data将是整型数据,String是字符串型数据。

其目的是利用MFCISAPI扩展的分析映射支持,并允许工作线程处理适当的请求。

要实现此目的,需创建以下两种结构:

structProcessNameData

{

EXTENSION_CONTROL_BLOCK*pECB;

CStringsFirstName;

CStringsLastName;

 

CStringsMiddleName;

};

structProcessSampleData

{

EXTENSION_CONTROL_BLOCK*pECB;

DWORDnValue;

CStringsString;

};

ProcessNameData结构将用于临时存放由MFC分析映射传递给ProcessName()函数的参数。

在ProcessName()函数内,动态分配一个新的ProcessNameData结构,并设置EXTENSION_CONTROL_BLOCK以及传递给我们的参数,然后将其传递给工作线程。

但是,将数据传递给工作线程时,必须使工作线程知道所传递的数据为ProcessNameData类型。

这可通过PostQueuedCompletionStatus()API中的第三个参数来实现。

在Default()函数中,将0作为第三个参数进行传递。

对于ProcessName(),传递的是1:

voidCExtThreadPoolExtension:

:

ProcessName(CHttpServerContext*pCtxt,

LPCTSTRszFirstName,

LPCTSTRszLastName,

LPCTSTRszMiddleName)

{

ProcessNameData*pData=newProcessNameData;

pData->pECB=pCtxt->m_pECB;

pData->sFirstName=szFirstName;

pData->sLastName=szLastName;

pData->sMiddleName=szMiddleName;

if(!

PostQueuedCompletionStatus(m_hIoPort,(DWORD)pData,

(DWORD)1,NULL))

{

charszBuffer[4096]={0};

sprintf(szBuffer,

"<H1>Errorpostingtocompletionport!

Win32Error=%i</H1>",

GetLastError());

DWORDdwBufLen=strlen(szBuffer);

pCtxt->m_pECB->WriteClient(pCtxt->m_pECB->ConnID,szBuffer,

&dwBufLen,0);

}

}

通过将值1传递给工作线程,工作线程将知道它需要处理的数据与Default()例程不同。

同样,对于ProcessSample()函数传递值2。

在此函数中,应创建一个新的ProcessSampleData结构实例。

在ThreadProc(工作线程所执行的实际例程)中,需要使所有线程阻塞在一个单独的完成端口(从GetExtensionVersion传递给线程例程)。

当线程调用GetQueuedCompletionStatus()时将结束这种状况。

如果未投递任何状态,则调用将一直阻塞,直至投递一个状态为止。

投递完成端口后,其中的一个工作线程将“醒来”并开始处理:

 

unsignedintThreadProc(void*p)

{

HANDLEIoPort=(HANDLE*)p;

unsignedlongpN1,pN2;

OVERLAPPED*pOverLapped;

while(GetQueuedCompletionStatus(IoPort,&pN1,&pN2,

&pOverLapped,INFINITE))

{

if(pOverLapped==(OVERLAPPED*)0xFFFFFFFF)

break;

else

{

switch(pN2)

{

case0:

DoWork((EXTENSION_CONTROL_BLOCK*)pN1);

break;

case1:

 

DoWork((ProcessNameData*)pN1);

break;

case2:

DoWork((ProcessSampleData*)pN1);

break;

default:

//Huh?

!

?

!

?

break;

}

}

}

return0;

}

可以看到,在switch()语句中,对正确的pN2值(该值是在PostQueuedCompletionStatus()调用中传递的第三个参数)进行了测试。

另外,还需要指出的是,DoWork()存在三个版本。

一个版本将采用EXTENSION_CONTROL_BLOCK指针,另一个版本采用ProcessNameData指针,而第三个版本采用ProcessSampleData指针。

通过显式地强制转换pN1,可以确保为提供的数据调用了正确的版本。

对于采用ProcessNameData和ProcessSampleData的DoWork()版本,需要在退出之前破坏这些结构。

这是因为这些结构创建的目的只是用于临时存放分析映射参数:

voidDoWork(ProcessNameData*pData)

{

EXTENSION_CONTROL_BLOCK*pECB=pData->pECB;

CStringsBody;

//BuildtheHTMLbody...

sBody.Format(

"<H1>Thisisthread%iinthreadpool.Hithere!

</H1>",

GetCurrentThreadId());

sBody+="<HR><H2>I'mprocessingthisfor";

sBody+=pData->sFirstName;

sBody+="";

if(pData->sMiddleName.CompareNoCase("~")!

=0)

{

sBody+=pData->sMiddleName;

sBody+="";

}

sBody+=pData->sLastName;

sBody+="</H2>";

//Pretendwe'respendingsometimeprocessingthisrequest

//thissampleassumes3seconds.

Sleep(3000);

SendData(pECB,sBody);

deletepData;

}

voidDoWork(ProcessSampleData*pData)

{

EXTENSION_CONTROL_BLOCK*pECB=pData->pECB;

CStringsBody;

//BuildtheHTMLbody...

sBody.Format(

"<H1>Thisisthread%iinthreadpool.Hithere!

</H1>",

GetCurrentThreadId());

sBody+="<HR><H2>Thisisthesampledata:

";

sBody+=pData->sString;

CStringsTmp;

sTmp.Format("(%i)</H2>",pData->nValue);

sBody+=sTmp;

//Pretendwe'respendingsometimeprocessingthisrequest

//thissampleassumes3seconds.

Sleep(3000);

SendData(pECB,sBody);

deletepData;

}

SendData()是一个将CString发送回客户端的简单函数。

但是,在开始处理之前,需要确保没有向线程发出终止信号。

这可通过测试pOverLapped指针的值来完成。

如果该重叠指针的值是0xFFFFFFFF(无效的句柄值),则说明线程将终止。

因此,例程将跳出while循环。

要向工作线程发出终止信号,可使用通过“MFCISAPI向导”创建的第三个虚函数:

TerminateExtension()。

在此函数中,需要向所有线程发出关闭信号,同时关闭完成端口:

BOOLCExtThread

PoolExtension:

:

TerminateExtension(DWORDdwFlags)

{

//extensionisbeingterminated

for(longn=0;n<m_nThreadCount;n++)

PostQueuedCompletionStatus(m_hIoPort,0,

0,(OVERLAPPED*)0xFFFFFFFF);

WaitForMultipleObjects(m_nThreadCount,m_phThreads,TRUE,120000);

CloseHandle(m_hIoPort);

delete[]m_phThreads;

returnTRUE;

}

要向所有线程发出关闭信号,可使用无效句柄值投递一个重叠指针。

对可用线程数执行此操作。

这将确保每个线程都收到该通知。

完成此操作后,通过对从AfxBeginThread()调用返回的句柄(这些句柄复制自

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1