线程池实现Word下载.docx

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

线程池实现Word下载.docx

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

线程池实现Word下载.docx

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-&

gt;

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&

lt;

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)

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&

#39;

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,&

quot;

&

H1&

Errorpostingtocompletionport!

Win32Error=%i&

/H1&

GetLastError());

DWORDdwBufLen=strlen(szBuffer);

pCtxt-&

m_pECB-&

WriteClient(pECB-&

ConnID,szBuffer,

amp;

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

DWORDnValue;

CStringsString;

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

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

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

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

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

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

ProcessName(CHttpServerContext*pCtxt,

LPCTSTRszFirstName,

LPCTSTRszLastName,

LPCTSTRszMiddleName)

ProcessNameData*pData=newProcessNameData;

pData-&

pECB=pCtxt-&

m_pECB;

sFirstName=szFirstName;

sLastName=szLastName;

sMiddleName=szMiddleName;

PostQueuedCompletionStatus(m_hIoPort,(DWORD)pData,

(DWORD)1,NULL))

sprintf(szBuffer,

Win32Error=%i&

GetLastError());

WriteClient(pCtxt-&

通过将值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;

switch(pN2)

case0:

DoWork((EXTENSION_CONTROL_BLOCK*)pN1);

case1:

DoWork((ProcessNameData*)pN1);

case2:

DoWork((ProcessSampleData*)pN1);

default:

//Huh?

!

?

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(

Thisisthread%iinthreadpool.Hithere!

GetCurrentThreadId());

sBody+=&

HR&

H2&

I&

mprocessingthisfor&

;

sBody+=pData-&

sFirstName;

&

if(pData-&

sMiddleName.CompareNoCase(&

~&

)!

=0)

sMiddleName;

sLastName;

/H2&

//Pretendwe&

respendingsometimeprocessingthisrequest

//thissampleassumes3seconds.

Sleep(3000);

SendData(pECB,sBody);

deletepData;

voidDoWork(ProcessSampleData*pData)

CStringsBody;

Thisisthesampledata:

sString;

CStringsTmp;

sTmp.Format(&

(%i)&

pData-&

nValue);

sBody+=sTmp;

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

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

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

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

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

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

TerminateExtension()。

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

BOOLCExtThread

PoolExtension:

TerminateExtension(DWORDdwFlags)

//extensionisbeingterminated

PostQueuedCompletionStatus(m_hIoPort,0,

0,(OVERLAPPED*)0xFFFFFFFF);

WaitForMultipleObjects(m_nThreadCount,m_phThreads,TRUE,120000);

CloseHandle(m_hIoPort);

delete[]m_phThreads;

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

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

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

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

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

当前位置:首页 > IT计算机 > 计算机软件及应用

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

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