CreateThread
在 Windows 中,您可以使用 CreateThread() 来创建线程,创建的线程在调用进程的虚拟地址空间中运行。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
SIZE_T dwStackSize, // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress, // thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags, // creation option
LPDWORD lpThreadId // thread identifier
);
lpThreadAttributes 是指向线程属性的指针,决定了线程句柄是否能由子进程继承。 需要include"thread.cpp"
// thread.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The _beginthread(), _beginthreadex(), _endthread(), and _endthreadex().
//
// There are several key differences in behavior between _beginthread() and
// _beginthreadex():
//
// * _beginthreadex() takes three additional parameters, which are passed on to
// CreateThread(): a security descriptor for the new thread, the initial
// thread state (running or asleep), and an optional out parameter to which
// the thread id of the newly created thread will be stored.
//
// * The procedure passed to _beginthread() must be __cdecl and has no return
// code. The routine passed to _beginthreadex() must be __stdcall and must
// return a return code, which will be used as the thread exit code.
// Likewise, _endthread() takes no parameter and always returns a thread exit
// code of 0 if the thread exits without error, whereas _endthreadex() takes
// an exit code.
//
// * _endthread() calls CloseHandle() on the handle returned from CreateThread().
// Note that this means that a caller should not use this handle, since it is
// possible that the thread will have terminated and the handle will have been
// closed by the time that _beginthread() returns.
//
// _endthreadex() does not call CloseHandle() to close the handle: the caller
// of _beginthreadex() is required to close the handle.
//
// * _beginthread() returns -1 on failure. _beginthreadex() returns zero on
// failure (just as CreateThread() does).
/ / thread.cpp
//
//微软公司版权所有。保留所有权利。
//
//使用_beginthread()、_beginthreadex()、_endthread()和_endthreadex()。
//
//在_beginthread()和。之间有几个关键的区别
/ / _beginthreadex ():
//
// * _beginthreadex()接受三个额外的参数,它们被传递给
// CreateThread():新线程(初始线程)的安全描述符
//线程状态(运行或休眠),以及一个可选的out参数
//将存储新创建线程的线程id。
//
// *传递给_beginthread()的过程必须是cdecl,没有返回值
/ /代码。传递给_beginthreadex()的例程必须是_stdcall和must
//返回一个返回码,它将被用作线程退出码。
同样,_endthread()不接受任何参数,并且总是返回一个线程出口
//如果线程没有错误退出,则为0,而_endthreadex()接受
//退出代码。
//
// * _endthread()在CreateThread()返回的句柄上调用CloseHandle()。
//注意,这意味着调用者不应该使用这个句柄,因为它是
//可能线程已经终止,句柄也已经结束
//在_beginthread()返回时关闭。
//
// _endthreadex()不调用CloseHandle()来关闭句柄:调用者
// of _beginthreadex()是关闭句柄所必需的。
//
// * _beginthread()在失败时返回-1。_beginthreadex()返回零
//失败(就像CreateThread()一样)。
由于历史原因,所以C/C++运行库并不是为多线程应用程序而设计的,所以为了保证其中的某些变量和函数的安全,那么必须创建一个数据结构,并使之与使用了C/C++运行库函数的每个线程所关联。当在调用C/C++运行库函数时,那些函数必须读取主调自己的线程的数据块,从而避免印象其他线程。
所以当编写C/C++代码时,请调用_beginthreadex,而不要使用CreateThread的大致原因就是如上所述。详细内容请继续往下读😀😺😃
_beginthreadex的函数参数列表与CreateThread一样,但是参数名和类型有所不同。这是因为C/C++程序不应该对Windows系统的类型有任何的依赖。
_beginthreadex也会返回新建线程的句柄,就像CreateThread一样。可以非常方便的替换自己程序中的CreateThread函数,但是数据类型不一样,所以还得添加一些类型转换即可。
————————————————
版权声明:本文为优快云博主「昨夜的秋天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/foreveyking/article/details/78029328
_beginthreadex
附加:绝不调用_beginthread。因为,
1._beginthread参数较少,不能创建具有安全属性的线程,不能让线程立即挂起等。_endthread也是如此,其退出代码被硬编码为0。
_endthread还存在一个问题,就是在调用ExitThread前,会调用CloseHandle,向其传入新线程的句柄。举个例子,比如你使用了_beginthread创建了一个新的线程,在其之后使用CloseHandle关闭线程句柄。由于_beginthread会调用_endthread函数终止线程,而线程内核对象的初始化计数为2,在_endthread函数中,调用ExitThread前,会调用CloseHandle,也就是说会连续两次递减引用计数,这时引用计数为0,内核对象被系统销毁,再之后在调用CloseHandle关闭线程句柄时,这个传入的句柄是无效的,即CloseHandle函数的调用会以失败告终。
所以请用_beginthreadex代替_beginthread,_endthreadex代替_endthread。 _beginthreadex调用的是_endthreadex,_beginthread调用的是_endthread
_beginthreadex内部使的是CreateThread
extern "C" uintptr_t __cdecl _beginthreadex(
void* const security_descriptor,
unsigned int const stack_size,
_beginthreadex_proc_type const procedure,
void* const context,
unsigned int const creation_flags,
unsigned int* const thread_id_result
)
{
_VALIDATE_RETURN(procedure != nullptr, EINVAL, 0);
unique_thread_parameter parameter(create_thread_parameter(procedure, context));
if (!parameter)
{
return 0;
}
DWORD thread_id;
HANDLE const thread_handle = CreateThread(
reinterpret_cast<LPSECURITY_ATTRIBUTES>(security_descriptor),
stack_size,
thread_start<_beginthreadex_proc_type>,
parameter.get(),
creation_flags,
&thread_id);
if (!thread_handle)
{
__acrt_errno_map_os_error(GetLastError());
return 0;
}
if (thread_id_result)
{
*thread_id_result = thread_id;
}
// If we successfully created the thread, the thread now owns its parameter:
parameter.detach();
return reinterpret_cast<uintptr_t>(thread_handle);
}
pthread_create
Linux 使用 pthread 库调用 pthread_create() 来派生线程:
int pthread_create (
pthread_t *thread_id,
pthread_attr_t *threadAttr,
void * (*start_address)(void *),
void * arg);
注意:在 Windows 中,受可用虚拟内存的限制,一个进程可以创建的线程数目是有限的。默认情况下,每个线程有一兆栈空间。因此,您最多可以创建 2,028 个线程。如果您减小默认栈大小,那么可以创建更多线程。在 Linux 中,使用 ULIMIT -a(limits for all users)可以获得每个用户可以创建的线程的最大数目,可以使用 ULIMIT -u 来修改它,不过只有在登录时才可以这样做。 /usr/Include/limit.h 和 ulimit.h 下的头文件定义了这些内容。您可以修改它们并重新编译内核,以使其永久生效。对于 POSIX 线程限制而言,local_lim.h 中定义的 THREAD_THREADS_MAX 宏定义了数目的上限。
指定线程函数
CreateThread() 中的 lpStartAddress 参数是刚创建的线程要执行的函数的地址。
pthread_create() 库调用的 start_address 参数是刚创建的线程要执行的函数的地址。
传递给线程函数的参数
在 Windows 中,系统调用 CreateThread() 的参数 lpParameter 指定了要传递给刚创建的线程的参数。它指明了将要传递给新线程的数据条目的地址。
在 Linux 中,库调用 pthread_create() 的参数 arg 指定了将要传递给新线程的参数。
AfxBeginThread
MFC专用的创建线程
UINT myproc(LPVOID lParam)
{
CITapTradeAPITestDlg *pWnd = (CITapTradeAPITestDlg *)lParam; //将窗口指针赋给无类型指针
pWnd->KMeansSegment(); //要执行的函数
return 1;
}
AfxBeginThread(myproc, (LPVOID)this);//启动新的线程
void CITapTradeAPITestDlg::KMeansSegment()
{}