CreateThread函数&&CString::GetBuffer函数
对这个两个常见的windows下的函数学习了一下:
//最简单的创建多线程实例 #include <stdio.h> #include <windows.h> //子线程函数 DWORD WINAPI ThreadFun(LPVOID pM) { printf("子线程的线程ID号为:%d\n子线程输出Hello World\n", GetCurrentThreadId()); return 0; } //主函数,所谓主函数其实就是主线程执行的函数。 int main() { printf(" 最简单的创建多线程实例\n"); printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n"); HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL); WaitForSingleObject(handle, INFINITE); return 0; }
第一个 CreateThread
函数功能:创建线程
函数原型:
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
函数说明:
第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
第四个参数是传给线程函数的参数。
第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数将返回线程的ID号,传入NULL表示不需要返回该线程ID号。
函数返回值:
成功返回新线程的句柄,失败返回NULL。
第二个 WaitForSingleObject
函数功能:等待函数 – 使线程进入等待状态,直到指定的内核对象被触发。
函数原形:
DWORD WINAPI WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
函数说明:
第一个参数为要等待的内核对象。
第二个参数为最长等待的时间,以毫秒为单位,如传入5000就表示5秒,传入0就立即返回,传入INFINITE表示无限等待。
因为线程的句柄在线程运行时是未触发的,线程结束运行,句柄处于触发状态。所以可以用WaitForSingleObject()来等待一个线程结束运行。
函数返回值:
在指定的时间内对象被触发,函数返回WAIT_OBJECT_0。超过最长等待时间对象仍未被触发返回WAIT_TIMEOUT。传入参数有错误将返回WAIT_FAILED
CreateThread()函数是Windows提供的API接口,在C/C++语言另有一个创建线程的函数_beginthreadex(),在很多书上(包括《Windows核心编程》)提到过尽量使用_beginthreadex()来代替使用CreateThread(),
原因:_beginthreadex()函数在创建新线程时会分配并初始化一个_tiddata块。这个_tiddata块自然是用来存放一些需要线程独享的数据。事实上新线程运行时会首先将_tiddata块与自己进一步关联起来。然后新线程调用标准C运行库函数如strtok()时就会先取得_tiddata块的地址再将需要保护的数据存入_tiddata块中。这样每个线程就只会访问和修改自己的数据而不会去篡改其它线程的数据了。因此,如果在代码中有使用标准C运行库中的函数时,尽量使用_beginthreadex()来代替CreateThread()
getbuffer是为了让你使用CString类中,保存字符串缓冲区的那块指针.
至于releasebuffer,在MSDN中有这样一句话.
If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before using any other CString member functions.
在对GetBuffer返回的指针使用之后需要调用ReleaseBuffer,这样才能使用其他Cstring的operations。否则会发生错误.
GetBuffer(0)返回的是指向CString对象所构造的字串指针,为GetBuffer(0)由系统自动计算字串所要的空间长度,但要求是规则的含\0字串,否则得自己给出长度,如str.GetBuffer(20)。
LPTSTR GetBuffer( int nMinBufLength ) 这个函数是CString 的一个比较实用的函数,请看如下示例:
GetBuffer(int nMinBufLength);的参数问题一直比较困扰人,网站的资料还像也不是太好给的,请看msdn解释
Parameters
nMinBufLength
The minimum size of the character buffer in characters. This value does not include space for a null terminator.
得到buffer的最小长度,当然这是由我们自己设定的一个参数,其原型定义如下:
LPTSTR CString::GetBuffer(int nMinBufLength)
{
ASSERT(nMinBufLength >= 0);
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
{
#ifdef _DEBUG
// give a warning in case locked string becomes unlocked
if (GetData() != _afxDataNil && GetData()->nRefs < 0)
TRACE0("Warning: GetBuffer on locked CString creates unlocked CString!/n");
#endif
// we have to grow the buffer
CStringData* pOldData = GetData();
int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
if (nMinBufLength < nOldLen)
nMinBufLength = nOldLen;
AllocBuffer(nMinBufLength);
memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
GetData()->nDataLength = nOldLen;
CString::Release(pOldData);
}
ASSERT(GetData()->nRefs <= 1);
// return a pointer to the character storage for this string
ASSERT(m_pchData != NULL);
return m_pchData;
}
上面的代码已经比较清楚了,当设定的长度小于原字符串长度时,nMinBufLength = nOldLen,然后分配相应的内存,当你设定的长度大于原字符串本身的长度时就要分配一块比较大的空间出来,这时你可以实现字符串对接的操作。
GetBuffer()主要作用是将字符串的缓冲区长度锁定,releaseBuffer则是解除锁定,使得CString对象在以后的代码中继续可以实现长度自适应增长的功能。
CString ::GetBuffer有两个重载版本:
LPTSTR GetBuffer( );LPTSTR GetBuffer(int nMinBufferLength);
在第二个版本中,当设定的长度小于原字符串长度时,nMinBufLength = nOldLen,该参数会被忽
略,不分配内存,指向原CString;当设定的长度大于原字符串本身的长度时就要重新分配(reallocate)一块比较大的空间出来。而调用第一个版本时,应如通过传入0来调用第二个版本一样。
是否需要在GetBufer后面调用ReleaseBuffer(),是根据你的后面的程序是否需要继续使用该字符串变量,并且是否动态改变其长度而定的。如果你GetBuffer以后程序自函数就退出,局部变量都不存在了,调用不调用ReleaseBuffer没什么意义了。
这是一个非常容易被用错的函数,主要可能是由于大家对它的功能不太了解。其实点破的话,也不是那么深奥。
GetBuffer(int size)是用来返回一个你所指定大小可写内存的成员方法。它和被重载的操作符LPCTSTR还是有点本质区别的,LPCTSTR是直接返回一个只读内存的指针,而GetBuffer则是返回一个可以供调用者写入的内存,并且,你可以给定大小。下面是个简单的,但也是非常典型的例子:
int readFile(CString& str, const CString& strPathName)
{
FILE* fp = fopen(strPathName, "r"); // 打开文件
fseek(fp, 0, SEEK_END);
int nLen = ftell(fp); // 获得文件长度
fseek(fp, 0, SEEK_SET); // 重置读指针
char* psz = str.GetBuffer(nLen);
fread(psz, sizeof(char), nLen, fp); //读文件内容
str.ReleaseBuffer(); //千万不能缺少
fclose(fp);
}
上面的函数是GetBuffer函数最典型的用法了,其实它就相当于申请一块nLen大小的内存,只不过,这块内存是被引用在CString对象的内部而已,这是非常有效的一种用法,如果不直接用GetBuffer函数来申请的话,那么你必须用new操作符(或者malloc()函数)在CString的外部申请,然后再将申请的内存拷贝到CString对象中,显然这是一个非常冗余的操作,会使你函数的效率大大下降。
ReleaseBuffer函数是用来告诉CString对象,你的GetBuffer所引用的内存已经使用完毕,现在必须对它进行封口,否则 CString将不会知道它现在所包含的字符串的长度,所以在使用完GetBuffer之后,必须立即调用ReleaseBuffer函数重置 CString的内部属性,其实也就是头部信息。
补充一下:
GetBuffer说白了就两个功能:
1:就是将CString里面的内存交到外部一个来处理,外部可以直接修改它的内容。
2:重新修改CString的内存大小,这个数值不包含null结尾符。
另一个典型的用法:就是将CString里面的内容变为int或long型,需要先获取里面的内存指针。这样就可以先GetBuffer(内存大小)方便直接转换。
如果在外部修改了CString里面的内容,在重新使用CString之前,需调用ReleaseBuffer()也就是说,ReleaseBuffer不需要每次都调用。
MSDN原文:
If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before using any other CSimpleStringT member methods.
The buffer memory is automatically freed when the CSimpleStringT object is destroyed.
If you keep track of the string length yourself, you should not append the terminating null character. You must, however, specify the final string length when you release the buffer with ReleaseBuffer. If you do append a terminating null character, you should pass –1 (the default) for the length toReleaseBuffer, and ReleaseBuffer will perform a strlen on the buffer to determine its length.