Windows进程通信——创建线程
2017-06-08 11:47
525 查看
1. 概述
在Windows环境下创建线程使用的方法主要由如下几种:CreateThread:CreateThread是Windows的API函数,提供操作系统级别的创建线程的操作,且仅限于工作者线程。不调用MFC和RTL的函数时,可以用CreateThread,其它情况不要轻易使用。在使用的过程中要考虑到进程的同步与互斥的关系(防止死锁)。
beginthread,beginthreadex:MS的C/C++运行期库函数,是对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常。然后,调用CreateThread真正创建线程。
AfxBeginThread:MFC中线程创建的MFC函数,首先创建了相应的CWinThread对象,然后调用CWinThread::CreateThread,在CWinThread::CreateThread中,完成了对线程对象的初始化工作,然后,调用_beginthreadex(AfxBeginThread相比较更为安全)创建线程。它简化了操作或让线程能够响应消息,即可用于界面线程,也可以用于工作者线程,但要注意不要在一个MFC程序中使用_beginthreadex()或CreateThread()。
2. 创建线程API和实例
1. CreateThread(),它的函数原型为HANDLE CreateThread( _In_ SEC_ATTRS SecurityAttributes, _In_ ULONG StackSize, _In_ SEC_THREAD_START StartFunction, _In_ PVOID ThreadParameter, _In_ ULONG CreationFlags, _Out_ PULONG ThreadId );lpThreadAttributes:
指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE
dwStackSize:
设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小
lpStartAddress:
指向线程函数的指针
lpParameter:
向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL
dwCreationFlags :
线程标志,可取值如下:
CREATE_SUSPENDED(0x00000004):
创建一个挂起的线程,线程不立即执行,需要调用ResumeThread函数才能执行
0:
表示创建后立即激活
lpThreadId:
保存新线程的id
使用实例
DWORD WINAPI my_tic_count(LPVOID m_pParam); //必须定义成这种格式 //CreateThread()调用的线程线程函数,没有延迟 HANDLE m_hCreateThread = ::CreateThread(NULL, //使用默认的安全属性 NULL, //使用的栈的大小,NULL使用创建线程进程的栈大小 my_tic_count, //线程函数的函数地址 &Total_num, //传递给线程的参数 NULL, //线程的启动类型 NULL); //新线程的id //方法1 CreateThread()调用的线程线程函数 DWORD WINAPI my_tic_count(LPVOID m_pParam) { cout << "CreateThread() count:" << endl; int* Total_num = (int*)m_pParam; for (int i=0; i<(*Total_num); ++i) { count = i; cout << count << "\t"; } cout << "\n"; return 0; }
2. _beginthread(),它的函数原型为
uintptr_t _beginthread(void(*start_address)(void *), unsigned stack_size, void *arglist );start_address:
新线程的起始地址 ,指向新线程调用的函数的起始地址
stack_size:
新线程的堆栈大小,可以为0
arglist:
传递给线程的参数列表,无参数是为NULL
使用需要添加#include <process.h>,使用实例
void my_beginthread(void* m_pParam); //线程函数需要定义成这样 HANDLE m_hbeginthread = (HANDLE)_beginthread(my_beginthread, NULL, &Total_num); //方法2 _beginthread()调用的线程线程函数 void my_beginthread(void* m_pParam) { cout << "_beginthread() count:" << endl; int* Total_num = (int*)m_pParam; for (int i = 0; i < (*Total_num); ++i) { count = i; cout << count << "\t"; } cout << "\n"; }
3. _beginthreadex(),它的函数原型为
uintptr_t _beginthreadex( // NATIVE CODE void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr );security:
安全属性,NULL为默认安全属性
stack_size:
指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0
start_address:
指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址)
arglist:
传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针
initflag:
线程初始状态,0:立即运行;CREATE_SUSPEND:suspended(悬挂)
thrdaddr:
用于记录线程ID的地址
使用实例:
unsigned int WINAPI my_beginthreadex(void* m_pParam); //线程函数需要定义成这样 HANDLE m_hbeginthreadex = (HANDLE)_beginthreadex(NULL, NULL, my_beginthreadex, &Total_num, CREATE_SUSPENDED, NULL); //方法3 _beginthreadex()调用的线程线程函数 unsigned int WINAPI my_beginthreadex(void* m_pParam) { cout << "_beginthreadex() count:" << endl; int* Total_num = (int*)m_pParam; for (int i = 0; i < (*Total_num); ++i) { count = i; cout << count << "\t"; } cout << "\n"; return 0; }
4. AfxBeginThread()。在MFC中,用户界面线程和工作者线程都是由AfxBeginThread创建的,MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别的函数原型为
工作线程:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );pfnThreadProc :
线程的入口函数,声明一定要如下: UINT MyThreadFunction(LPVOID pParam),不能设置为NULL
pParam :
传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
nPriority :
线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级.
nStackSize :
指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈
dwCreateFlags :
指定创建线程以后,线程有怎么样的标志.可以指定两个值:
CREATE_SUSPENDED :
线程创建以后,会处于挂起状态,直到调用:ResumeThread
0 :
创建线程后就开始运行.
lpSecurityAttrs :
指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL
返回值:
一个指向新线程的线程对象的指针
界面线程:
CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );pThreadClass:
从CWinThread派生的RUNTIME_CLASS类;
nPriority:
指定线程优先级,如果为0,则与创建该线程的线程相同;
nStackSize:
指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
dwCreateFlags:
一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
lpSecurityAttrs:
表示线程的安全属性,NT下有用。
使用实例
UINT MyThreadFunction(LPVOID m_pParam); ::AfxBeginThread(MyThreadFunction, NULL, NULL, NULL, NULL); //方法4 AfxBeginThread()调用的线程线程函数 UINT MyThreadFunction(LPVOID m_pParam) { cout << "_beginthreadex() count:" << endl; int* Total_num = (int*)m_pParam; for (int i = 0; i < (*Total_num); ++i) { count = i; ... } return 0; }
补充:
在上面的示例中我都将返回值转换为了HANDLE类型,目的无非是为了得到现成的资源标识,进而方便进行管理。在对线程进行管理的时候这里使用的API主要有ResumeThread函数、SuspendThread函数和ExitThread()函数,这些函数在字面意思已经很清楚了,分别是回复线程、线程暂停、结束线程。但是在实际使用过程中下面的两点需要注意:
(1)对于ResumeThread函数
在暂停状态中创建一个线程,就能够在线程有机会执行任何代码之前改变线程的运行环境(如优先级)。一旦改变了线程的环境,必须使线程成为可调度线程。要进行这项操作,可以调用ResumeThread,将调用CreateThread函数时返回的线程句柄传递给它(或者是将传递给CreateProcess的ppiProcInfo参数指向的线程句柄传递给它):如果ResumeThread函数运行成功,它将返回线程的前一个暂停计数,否则返回0xFFFFFFFF。单个线程可以暂停若干次。如果一个线程暂停了3次,它必须恢复3次,然后它才可以被分配给一个CPU。
(2)对于SuspendThread函数
当创建线程时,除了使用 CRETE_SUSPENDED也可以调用SuspendThread函数来暂停线程的运行:任何线程都可以调用该函数来暂停另一个线程的运行(只要拥有线程的句柄)。线程可以自行暂停运行,但是不能自行恢复运行。与ResumeThread一样SuspendThread返回的是线程的前一个暂停计数。线程暂停的最多次数可以是MAXIMUM_SUSPEND_COUNT次(在WinNT. h中定义为127)。注意,SuspendThread与内核方式的执行是异步进行的,但是在线程恢复运行之前,不会发生用户方式的执行。在实际环境中,调用SuspendThread时必须小心,因为不知道暂停线程运行时它在进行什么操作。如果线程试图从堆栈中分配内存,那么该线程将在该堆栈上设置一个锁。当其他线程试图访问该堆栈时,这些线程的访问就被停止,直到第一个线程恢复运行。
相关文章推荐
- windows下进程和线程的创建
- linux、windows下的进程间的通信方式、线程间的通信方式
- 详细介绍windows系统下创建进程、线程的过程
- Linux下创建进程线程以及通信技术的使用
- Windows线程+进程通信
- Linux/windows 进程/线程间通信机制
- windows C++进程间和线程间通信
- windows下进程和线程的关系、通信方式
- windows和linux进程间与线程间通信
- 进程及线程创建流程
- 将 Windows IPC 应用程序移植到 Linux,第 1 部分: 进程和线程
- 翻译: Windows Internals: 第六章: 进程创建流程
- 推荐一个线程进程(内部)通信框架--ThreadMessaging.NET
- Windows下进程通信方式[转]
- PsTools软件是用来在windows平台下来查看进程与线程情况
- Windows下进程通信的几种方式
- Windows系统单个进程最多线程数2000个?
- Windows进程/线程浅谈
- Windows系列操作系统对线程间通信的支持 ——管道与邮路
- Windows系列操作系统对线程间通信的支持——管道与邮路