Win32 多线程的创建方法和基本使用 .

简介: Summary: 总结Win32提供的创建多线程相关的API接口和基本的使用框架。 Ref: MSDN: http://msdn.microsoft.com/zh-cn/library/y6h8hye8(v=VS.

Summary:

总结Win32提供的创建多线程相关的API接口和基本的使用框架。

Ref:

MSDN: http://msdn.microsoft.com/zh-cn/library/y6h8hye8(v=VS.100)

Win32多线程的创建方法主要有:

(1)CreateThread()

(2)_beginthread()&&_beginthreadex()

(3)AfxBeginThread()

(4)CWinThread类

(1)CreateThread()

百度百科:http://baike.baidu.com/view/1191444.htm

函数原型:

view plaincopy to clipboardprint?
01.HANDLE CreateThread(
02.  LPSECURITY_ATTRIBUTES lpThreadAttributes,
03.  DWORD dwStackSize,
04.  LPTHREAD_START_ROUTINE lpStartAddress,
05.  LPVOID lpParameter,
06.  DWORD dwCreationFlags,
07.  LPDWORD lpThreadId);
08.}
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  DWORD dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  DWORD dwCreationFlags,
  LPDWORD lpThreadId);
}

头文件:Windows.h

CreateThread是Win32提供的创建线程的最基础的API,用于在主线程上创建一个线程。返回一个HANDLE句柄(内核对象)。

参数简要说明:

lpThreadAttributes:线程属性,用于设置线程的属性,NULL表示使用默认的设置。dwStackSize:线程堆栈大小,使用0采用默认设置,windows会根据需要动态增加堆栈大小。lpStartAddress:指向线程函数的指针。lpParameter:向线程函数传递的参数。dwCreationFlags:线程标志,CREATE_SUSPENDED表示创建一个挂起的线程,0表示创建后立即激活线程。lpThreadId,先线程的ID(输出参数)。

创建线程的代码:

view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.
04.DWORD WINAPI ThreadProc(LPVOID lpParam)
05.{
06. printf("sub thread started\n");
07. printf("sub thread finished\n");
08. return 0;
09.}
10.
11.int main(int argc, char* argv[])
12.{
13. DWORD threadID;
14. HANDLE hThread;
15. hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程
16.
17. return 0;
18.}
#include "stdafx.h"
#include

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
printf("sub thread started\n");
printf("sub thread finished\n");
return 0;
}

int main(int argc, char* argv[])
{
DWORD threadID;
HANDLE hThread;
hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程

return 0;
}
如果直接使用上面的代码,那么很可能没有任何输出,这是由于主线程创建了子线程后主线程继续向下运行,子线程还没来得及执行里面的代码主线程可能就结束了。这就需要另一个API来进行同步:WaitForSingleObject()。

与之对应的还有WaitForMultipleObjects,用于同步一组内核对象。(参考http://msdn.microsoft.com/zh-cn/site/ms686360获取所有的同步函数(Synchronization Functions)的使用。

WaitForSingleObject原型:DWORD WINAPI WaitForSingleObject(__in HANDLE hHandle, __in DWORD dwMilliseconds);其中,第一个参数是要等待的内核对象的句柄,第二个参数是设置等待超时时间,可以设置为INFINITE,表示一直等待直到有信号触发。

在内核对象使用完毕后,一般需要关闭,使用CloseHandle()函数,参数为内核对象句柄。

所以,以下是一个最基本的使用CreateThread的例子:

view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.
04.DWORD WINAPI ThreadProc(LPVOID lpParam)
05.{
06. printf("sub thread started\n");
07. // TODO: Add your thread code here.
08. printf("sub thread finished\n");
09. return 0;
10.}
11.
12.int main(int argc, char* argv[])
13.{
14. DWORD threadID;
15. HANDLE hThread;
16. hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程
17.
18. WaitForSingleObject(hThread,INFINITE);
19. CloseHandle(hThread); // 关闭内核对象
20.
21. return 0;
22.}
#include "stdafx.h"
#include

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
printf("sub thread finished\n");
return 0;
}

int main(int argc, char* argv[])
{
DWORD threadID;
HANDLE hThread;
hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程

WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread); // 关闭内核对象

return 0;
}
(2)_beginthread()&&_beginthreadex()

百度百科:http://baike.baidu.com/view/3029167.htm

MSDN:http://msdn.microsoft.com/zh-cn/library/kdzttdcb.aspx

函数原型:

view plaincopy to clipboardprint?
01.uintptr_t _beginthread( // NATIVE CODE
02. void( __cdecl *start_address )( void * ),
03. unsigned stack_size,
04. void *arglist
05.);
uintptr_t _beginthread( // NATIVE CODE
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
);

头文件:process.h

参数说明:第一个参数是线程函数的指针,第二个参数是堆栈大小,第三个参数是要传递给线程函数的参数列表。返回值也是线程句柄(关于更多说明,参考MSDN)。
同样,对于_beginthread()的同步,和CreateThread一样可以使用WaitForSingleObject函数,CloseHandle()关闭内核对象。另外,_beginthread()的线程函数是无返回值类型的,可以使用_endthread()在线程函数中结束线程。

下面是一个使用_beginthread()的基本的例子:

view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.#include
04.
05.void __cdecl ThreadProc(void *para)
06.{
07. printf("sub thread started\n");
08. // TODO: Add your thread code here.
09. printf("sub thread finished\n");
10. _endthread(); // 可以省略,隐含会调用。
11.}
12.
13.int main(int argc, char* argv[])
14.{
15. HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);
16.
17. WaitForSingleObject(hThread,INFINITE);
18. CloseHandle(hThread);
19.}
#include "stdafx.h"
#include
#include

void __cdecl ThreadProc(void *para)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
printf("sub thread finished\n");
_endthread(); // 可以省略,隐含会调用。
}

int main(int argc, char* argv[])
{
HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);

WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread);
}另外,还有一个函数_beginthreadex(),可以简单的认为_beginthread()为其简化版,所以更多的时候是使用更简单的_beginthread()了。
说明:在MSDN中可以看到一句很重要的提示,内容为“For an executable file linked with Libcmt.lib, do not call the Win32 ExitThread API; this prevents the run-time system from reclaiming allocated resources. _endthread and _endthreadex reclaim allocated thread resources and then call ExitThread.”,简单翻译就是说,对于链接Libcmt.lib的可执行程序,不要使用Win32的线程退出函数(ExitThread),这会阻止运行时系统回收分配的资源,应该使用_endthread,它能回收分配的线程资源然后调用ExitThread。这个问题看似没有提到CreateThread(),但是其实有关,这就是经常看到有些资料上坚决的说到”不要使用CreateThread创建线程,否则会内存泄漏“的来源了。

问题引出:CreateThread的内存泄漏问题(CreateThread和_beginthread的区别)

Related Topics:http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html http://www.cnblogs.com/whiteyun/archive/2011/06/02/2067742.html ....

1. _beginthread也是通过CreateThread来创建线程的,只是_beginthread对其进行了一些封装,将相关”资源“通过线程的本地存储(TLS)传递给了线程函数的参数,然后在调用_endthread的时候,会将这些保存的资源进行释放。

2. 并不是所有的使用CreateThread的情况都会有内存泄漏。看了很多人的文章,只有http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html的分析是最清晰的,我已经转到http://dl.dbank.com/c03ljl2iud了,可下载查看(版权归原作者所有)。

总之,建议是使用_beginthread取代CreateThread来创建线程。

(3)AfxBeginThread():

很显然,这是MFC中的Afx系列函数,一个在MFC中创建线程的全局函数。由于现在也不怎么用MFC了,这里就不多说了。

(4)CWinThread类:

很显然,是MFC中创建线程的类,同上,不多说了。

欢迎补充!

(1)补充内容:

关于WaitForMultipleObjects在_beginthread无法使用的问题

问题:使用_beginthread创建多个线程,无法使用WaitForMultipleObjects来进行同步。

这个问题可以用下面的例子来测试:

view plaincopy to clipboardprint?
01.#include "stdafx.h"
02.#include
03.#include
04.
05.void __cdecl ThreadProc(void *para)
06.{
07. printf("sub thread started\n");
08. // TODO: Add your thread code here.
09. printf("sub thread finished\n");
10. _endthread(); // 可以省略,隐含会调用。
11.}
12.
13.int main(int argc, char* argv[])
14.{
15. DWORD threadID;
16. HANDLE hThread[10];
17. for(int i =0;i<10;i++)
18. hThread[i] = (HANDLE)_beginthread(ThreadProc,0,NULL);
19.
20. WaitForMultipleObjects(10, hThread,TRUE,INFINITE); //无法同步所有线程!
21. for(int i = 0;i<10;i++) {
22. CloseHandle(hThread);
23. }
24.}
#include "stdafx.h"
#include
#include

void __cdecl ThreadProc(void *para)
{
printf("sub thread started\n");
// TODO: Add your thread code here.
printf("sub thread finished\n");
_endthread(); // 可以省略,隐含会调用。
}

int main(int argc, char* argv[])
{
DWORD threadID;
HANDLE hThread[10];
for(int i =0;i<10;i++)
hThread[i] = (HANDLE)_beginthread(ThreadProc,0,NULL);

WaitForMultipleObjects(10, hThread,TRUE,INFINITE); //无法同步所有线程!
for(int i = 0;i<10;i++) {
CloseHandle(hThread);
}
}
期望的结果是程序能输出10次的线程创建结束的消息,但是实际运行发现,无法达到这么多次数。为何?这是因为WaitForMultipleObjects在这里无法正常工作。原因是:

_endthread()在结束线程的时候,会自动调用CloseHandle关闭内核对象。这就容易解释了,如果提前关闭了内核对象,WaitForMultipleObjects会返回错误。那么有没有专门用于_beginthread创建的线程的同步方法呢,就我目前所知,好象是没有的!要解决这里的问题,可以分别调用WaitForSingleObject来同步,当然,使用其他一些变相的方法也是可以的,另外,上面的CloseHandle当然就可以不用再调用了。

总结:看来_beginthread也不是那么好用。:)

目录
相关文章
|
7月前
|
存储 Oracle Java
|
9月前
|
Java
创建线程的方法
Java中实现多线程有四种方式:1. 继承Thread类,简单但占用继承机会,耦合度高;2. 实现Runnable接口,推荐方式,任务与线程解耦,支持Lambda;3. 实现Callable接口配合FutureTask,可获取返回值和异常;4. 使用线程池(ExecutorService),企业推荐,管理线程生命周期,提升性能,支持多种线程池类型。
250 1
|
10月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
708 5
|
监控 Java
捕获线程执行异常的多种方法
【10月更文挑战第15天】捕获线程执行异常的方法多种多样,每种方法都有其特点和适用场景。在实际开发中,需要根据具体情况选择合适的方法或结合多种方法来实现全面有效的线程异常捕获。这有助于提高程序的健壮性和稳定性,减少因线程异常带来的潜在风险。
572 156
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
327 3
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
187 2
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
266 1
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
390 1
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
152 1

热门文章

最新文章