|
Ruediger R. Asche
Microsoft Developer Network 技术小组
ConcurrentExecution 的内部工作
请注意:本节的讨论是非常技术性的,所以假设您理解很多有关 Win32 线程 API 的知识。如果您对如何使用 ConcurrentExecution 类来收集测试数据更加感兴趣,而不是对 ConcurrentExecution::DoForAllObjects 是如何被实现的感兴趣,那么您现在就可以跳到下面的“使用
ConcurrentExecution 来试验线程性能”一节。
让我们从 DoSerial 开始,因为它很大程度上是一个“不费脑筋的家伙”:
BOOL ConcurrentExecution::DoSerial(int iNoOfObjects,long *ObjectArray,
CONCURRENT_EXECUTION_ROUTINE pProcessor,
CONCURRENT_FINISHING_ROUTINE pTerminator)
{
for (int iLoop=0;iLoop<iNoOfObjects;iLoop++)
{
pTerminator((LPVOID)ObjectArray[iLoop],(LPVOID)pProcessor((LPVOID)ObjectArray[iLoop]));
};
return TRUE;
};
这段代码只是循环遍历该数组,在每一次迭代中调用处理器,然后在处理器和对象本身的结果上调用终结器。干得既干净又漂亮,不是吗?
令人感兴趣的成员函数是 DoForAllObjects。乍一看,DoForAllObjects 所要做的也没有什么特别的——请求操作系统创建为每一个计算一个线程,并且确保终结器函数能够被正确地调用。但是,有两个问题使得 DoForAllObjects 比它的表面现象要复杂:第一,当计算的数目多于可用的线程数时,ConcurrentExecution 的一个实例所创建的“并发的最大度数”参数可能需要一些附加的记录(bookkeeping)。第二,每一个计算的终结器函数都是在调用 DoForAllObjects 的线程的上下文中被调用的,而不是在该计算运行所处的线程上下文中被调用的;并且,终结器是在处理器结束之后立刻被调用的。要处理这些问题还是需要很多技巧的。
让我们深入到代码中,看看究竟是怎么样的。该段代码是从文件 Thrdlib.cpp 中继承来的,但是为了清除起见,已经被精简了:
int ConcurrentExecution::DoForAllObjects(int iNoOfObjects,long *ObjectArray,
CONCURRENT_EXECUTION_ROUTINE pObjectProcessor,
CONCURRENT_FINISHING_ROUTINE
pObjectTerminated)
{
int iLoop,iEndLoop;
DWORD iThread;
DWORD iArrayIndex;
DWORD dwReturnCode;
DWORD iCurrentArrayLength=0;
BOOL bWeFreedSomething;
char szBuf[70];
m_iCurrentNumberOfThreads=iNoOfObjects;
HANDLE *hPnt=(HANDLE
*)VirtualAlloc(NULL,m_iCurrentNumberOfThreads*sizeof(HANDLE)
,MEM_COMMIT,PAGE_READWRITE);
for(iLoop=0;iLoop<m_iCurrentNumberOfThreads;iLoop++)
hPnt[iLoop] = CreateThread(NULL,0,pObjectProcessor,(LPVOID)ObjectArray[iLoop],
CREATE_SUSPENDED,(LPDWORD)&iThread);
首先,我们为每一个对象创建单独的线程。因为我们使用 CREATE_SUSPENDED 来创建该线程,所以还没有线程被启动。另一种方法是在需要时创建每一个线程。我决定不使用这种替代的策略,因为我发现当在一个同时运行了多个线程的应用程序中调用时, CreateThread 调用是非常浪费的;这样,同在运行时创建每一个线程相比,在此时创建线程的开销将更加容易接受,
for (iLoop = 0; iLoop < m_iCurrentNumberOfThreads; iLoop++)
{
HANDLE hNewThread;
bWeFreedSomething=FALSE;
// 如果数组为空,分配一个 slot 和 boogie。
if (!iCurrentArrayLength)
{
iArrayIndex = 0;
iCurrentArrayLength=1;
}
else
{
// 首先,检查我们是否可以重复使用任何的 slot。我们希望在查找一个新的
slot 之前首先// 做这项工作,这样我们就可以立刻调用该就线程的终结器...
iArrayIndex=WaitForMultipleObjects(iCurrentArrayLength,
m_hThreadArray,FALSE,0);
if (iArrayIndex==WAIT_TIMEOUT) // no slot free...
{
{
if (iCurrentArrayLength >= m_iMaxArraySize)
{
iArrayIndex= WaitForMultipleObjects(iCurrentArrayLength,
m_hThreadArray,FALSE,INFINITE);
bWeFreedSomething=TRUE;
}
else // 我们可以释放某处的一个 slot,现在就这么做...
{
iCurrentArrayLength++;
iArrayIndex=iCurrentArrayLength-1;
}; // Else iArray |