createthread函数(createthread函数传递参数)
CreateThread()函数每个参数都是什么意思,线程问题帮我都解释一下好么!
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpsa,
DWORD cbStack,
LPTHREAD_START_ROUTINE lpStartAddr,
LPVOID lpvThreadParam,
DWORD fdwCreate,
LPDWORD lpIDThread
);
lpsa:线程句柄的安全属性,比如子进程是否可以继承这个线程句柄,一般情况设置为NULL
cbStack:线程栈大小,一般取0表示默认大小
lpStartAddr:线程入口函数 typedef DWORD (__stdcall *LPTHREAD_START_ROUTINE) (
void* lpThreadParameter );在win32程序中默认的调用函数约定就是WINAPI ,__stdcall = WINAPI 。因此你可以声明你的入口函数为:
DWORD WINAPI ThreadProc( void* lpParamete) {//线程中你要做的事情}
lpvThreadParam:就是线程入口函数的参数,就是ThreadProc( void* lpParamete) 的参数
fdwCreate:控制线程创建的标志一般为0,表示线程立即启动。如果你想创建之后把线程挂起来可以传入CREATE_SUSPENDED ,传入这个参数你需要再适当的地方调用ResumeThread 启动线程
lpIDThread:是线程ID返回值,这个用来接收线程返回的ID
写了这么多我还是怕你不懂:
我举个例子吧,我不保证能运行成功,我也没去编译
DWORD WINAPI ThreadProc( void* lpParameter)
{
int *x = (int*)lpParameter;//获得参数的地址
MessageBox(NULL,TEXT("adf"),NULL,MB_OK);
}
DWORD dwThreadID;
int x = 0;
HANDLE hThread = CreateThread(0,0,ThreadProc,(void*)x,0,dwThreadID);
CloseHandle(hThread);
想学习好windows编程MSDN是必须了解的,你居然不知道什么是MSDN,MSDN就是帮助文档,你可以到百度搜一下并把它下载过来,这对你MFC、Windows API、C/C++等很有帮助的
如何避免使用CreateThread函数导致的内存泄露
CreateThread导致内存泄露的原因
这得从C运行时库说起了。
VC运行时库,有一个宏errno,用来获得上一步操作的错误码,类似于Win32中的GetLastError()函数。在多线程环境下,不同线程调用errno返回的都是caller线程的错误码,绝对不会混淆,这是因为使用了TLS技术。
TLS,Thread Local Storage,是用来存取线程相关数据的一种技术,在Win32中由操作系统的Tls*系列函数提供支持。例如,可以在程序开始的地方调用TlsAlloc()函数,获得一个TLS index,这个index在进程范围内有效,然后可以创建n个线程,在每个线程中使用TlsSetValue(index,data)将线程相关数据和index关联起来,使用TlsGetValue(index)来获取当前线程和index相关联的的线程相关数据。
查看msdn可以发现,Tls*函数的定义如下:
[cpp] view plaincopy
DWORD WINAPI TlsAlloc(void);
BOOL WINAPI TlsSetValue(
__in DWORD dwTlsIndex,
__in LPVOID lpTlsValue
);
LPVOID WINAPI TlsGetValue(
__in DWORD dwTlsIndex
);
BOOL WINAPI TlsFree(
__in DWORD dwTlsIndex
);
观察TlsSetValue/TlsGetValue的原型可以发现,与index关联的数据只能是void *类型,因此通常的做法是在线程开始的时候,为这个线程分配一块内存,用于存储所有与线程相关的数据,然后把这块内存的起始地址与index关联起来。如果这块内存在线程退出的时候没有释放掉,那就有内存泄露的危险。
回到errno,来看看C运行时库是如何实现errno的。
errno的声明和实现如下:
[c-sharp] view plaincopy
/* error.h - errno的声明 */
_CRTIMP extern int * __cdecl _errno(void);
#define errno (*_errno())
/* dosmap.c - errno的实现 */
int * __cdecl _errno(
void
)
{
_ptiddata ptd = _getptd_noexit();
if (!ptd) {
return ErrnoNoMem;
} else {
return ( ptd-_terrno );
}
}
观察_errno的代码,函数首先调用了_getptd_noexit()函数,这个函数的代码如下:
[cpp] view plaincopy
/* tiddata.c - _getptd_noexit()实现 */
_ptiddata __cdecl _getptd_noexit (
void
)
{
_ptiddata ptd;
DWORD TL_LastError;
TL_LastError = GetLastError();
#ifdef _M_IX86
/*
* Initialize FlsGetValue function pointer in TLS by calling __set_flsgetvalue()
*/
if ( (ptd = (__set_flsgetvalue())(__flsindex)) == NULL ) {
#else /* _M_IX86 */
if ( (ptd = FLS_GETVALUE(__flsindex)) == NULL ) {
#endif /* _M_IX86 */
/*
* no per-thread data structure for this thread. try to create
* one.
*/
#ifdef _DEBUG
extern void * __cdecl _calloc_dbg_impl(size_t, size_t, int, const char *, int, int *);
if ((ptd = _calloc_dbg_impl(1, sizeof(struct _tiddata), _CRT_BLOCK, __FILE__, __LINE__, NULL)) != NULL) {
#else /* _DEBUG */
if ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) != NULL) {
#endif /* _DEBUG */
if (FLS_SETVALUE(__flsindex, (LPVOID)ptd) ) {
/*
* Initialize of per-thread data
*/
_initptd(ptd,NULL);
ptd-_tid = GetCurrentThreadId();
ptd-_thandle = (uintptr_t)(-1);
}
else {
/*
* Return NULL to indicate failure
*/
_free_crt(ptd);
ptd = NULL;
}
}
}
SetLastError(TL_LastError);
return(ptd);
}
_getptd_noexit()函数首先通过TLS查找线程相关数据,如果没有找到,就分配一块内存,存放_tiddata结构,并将这块内存与__flsindex相关联。由此可见,errno的确使用了TLS技术,而且通过查找_getptd_noexit() 可以发现,VC运行时库中很多很多函数都使用了TLS,errno只不过是其中的一个典型。
如何在VC中利用系统函数创建一个新的线程
CreateThread函数可以用来创建一个线程,在MSDN中查找这个函数得到如下信息:"The CreateThread function creates a thread to execute within the address space of the calling process."和"If the function succeeds, the return value is a handle to the new thread."所以我们得定义一个句柄用来存放它的返回值。还定义一个指向线程ID的DWORD值dwThreadId。然后我们就可以用CreateThread函数来创建我们的线程了,CreateThread函数有六个参数分别是
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes
DWORD dwStackSize, // initial thread stack size
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to receive thread ID
其中第一个参数我们设置为NULL,使这个句柄不能被继承;第二个参数设置为0,使用默认的堆栈大小;第三个参数为线程函数的起始地址,也就是线程函数的函数名;第四个参数为NULL,没有值要传递给线程函数;第五个参数为0,创建好之后马上让线程运行;第六个参数设置为指向线程ID的地址。创建好线程之后,线程函数进行初始化之类的操作,主函数继续执行,此时可以输出被创建线程的ID。我们在主函数中用WaitForSingleObject函数来等待线程函数变成受信(signaled)状态,它的两个参数分别是
HANDLE hHandle, // handle to object to wait for
DWORD dwMilliseconds // time-out interval in milliseconds
第一参数为线程函数的句柄,第二个参数设置为INFINITE,等待线程一直执行完。在程序的最后还要记得用CloseHandle函数关闭线程,这样主函数就写完了。
在线程函数里面我们可以简单地做一些工作,比如设置一个循环,让它输出一定的信息等。源程序如下:
#include windows.h
#include iostream.h
DWORD WINAPI ThreadFunc(HANDLE Thread)
{
int i;
for(i=0;i10;i++)
{
cout"A new thread has created!"endl;
}
return 0;
}
int main(int argc,char* argv[])
{
HANDLE Thread;
DWORD dwThreadId;
Thread=::CreateThread
(NULL,0,ThreadFunc,NULL,0,dwThreadId);
cout"The new thread ID is :"dwThreadIdendl;
::WaitForSingleObject(Thread,INFINITE);
::CloseHandle(Thread);
return 0;
}
在Window xp sp2VC++ 6.0环境下编译通过。