浅谈一下C++中的线程创建

如同标题所说

这个文章讲的内容将会很基础

只会涉及线程创建和同步等等

不会涉及过难的知识


所需文件头(windows.h stdio.h)

接下来是CreateThread的定义以及参数详解

HANDLE CreateThread(
                    LPSECURITY_ATTRIBUTES lpThreadAttributes,
                    DWORD dwStackSize,
                    LPTHREAD_START_ROUTINE lpStartAddress,
                    LPVOID lpParameter,
                    DWORD dwCreationFlags,
                    LPDWORD lpThreadID
                   );
/*参数的含义如下:
lpThreadAttrivutes:指向SECURITY_ATTRIBUTES的指针,用于定义新线程的安全属性,一般设置成NULL;
dwStackSize:分配以字节数表示的线程堆栈的大小,默认值是0;
lpStartAddress:指向一个线程函数地址。每个线程都有自己的线程函数,线程函数是线程具体的执行代码;
lpParameter:传递给线程函数的参数;
dwCreationFlags:表示创建线程的运行状态,其中CREATE_SUSPEND表示挂起当前创建的线程,而0表示立即执行当前创建的进程;
lpThreadID:返回新创建的线程的ID编号*/

对于初学者来说,一定会觉得这样有点复杂,别急,我们先了解下面几个函数

BOOL TerminateThread(
  [in, out] HANDLE hThread,
  [in]      DWORD  dwExitCode
);
/*parameters
[in, out] hThread

要终止的线程的句柄。

句柄必须具有 THREAD_TERMINATE 访问权限。 有关详细信息,请参阅 线程安全和访问权限。

[in] dwExitCode

线程的退出代码。 使用 GetExitCodeThread 函数检索线程的退出值。

返回值
如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

注解
TerminateThread 用于导致线程退出。 发生这种情况时,目标线程没有机会执行任何用户模式代码。 附加到线程的 DLL 不会收到线程正在终止的通知。 系统释放线程的初始堆栈。

Windows Server 2003 和 Windows XP: 目标线程的初始堆栈未释放,从而导致资源泄漏。

TerminateThread 是一个危险的函数,只能在最极端的情况下使用。 仅当确切地知道目标线程正在执行的操作,并且控制目标线程可能在终止时运行的所有代码时,才应调用 TerminateThread 。 例如, TerminateThread 可能会导致以下问题:

如果目标线程拥有关键节,则不会释放关键节。
如果目标线程正在从堆分配内存,则不会释放堆锁。
如果目标线程在终止时正在执行某些 kernel32 调用,则线程进程的 kernel32 状态可能不一致。
如果目标线程正在操作共享 DLL 的全局状态,则 DLL 的状态可能会被销毁,从而影响 DLL 的其他用户。
线程无法保护自身免受 TerminateThread 的侵害,而不是通过控制对其句柄的访问。 CreateThread 和 CreateProcess 函数返回的线程句柄具有THREAD_TERMINATE访问权限,因此任何持有这些句柄之一的调用方都可以终止线程。
如果目标线程是调用此函数时进程的最后一个线程,则线程的进程也会终止。

线程对象的状态将发出信号,释放已等待线程终止的任何其他线程。 线程的终止状态从 STILL_ACTIVE 更改为 dwExitCode 参数的值。

终止线程不一定从系统中删除线程对象。 关闭最后一个线程句柄时,将删除线程对象。*/

这个函数用来终结线程,这个函数是比较危险的,须等到同步执行完后再使用



BOOL CloseHandle(
  [in] HANDLE hObject
);
/*
parameters
[in] hObject

打开对象的有效句柄。

返回值
如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

如果应用程序在调试器下运行,则函数在收到无效的句柄值或伪句柄值时将引发异常。 如果关闭句柄两次,或者对 FindFirstFile 函数返回的句柄调用 CloseHandle 而不是调用 FindClose 函数,则可能会发生这种情况。

注解
CloseHandle 函数关闭以下对象的句柄:

访问令牌
通信设备
控制台输入
控制台屏幕缓冲区
事件
文件
文件映射
I/O 完成端口
作业
Mailslot
内存资源通知
Mutex
命名管道
管道
进程
Semaphore
线程
事务
可等待计时器
创建这些对象的函数的文档指出,完成对象后应使用 CloseHandle ,以及关闭句柄后对对象挂起的操作会发生什么情况。 通常, CloseHandle 会使指定的对象句柄失效,减少对象的句柄计数,并执行对象保留检查。 关闭对象的最后一个句柄后,将从系统中删除该对象。 有关这些对象的创建者函数的摘要,请参阅 内核对象。
通常,应用程序应为每个打开的句柄调用 CloseHandle 一次。 如果使用句柄的函数因ERROR_INVALID_HANDLE而失败,通常不需要调用 CloseHandle ,因为此错误通常表示句柄已无效。 但是,某些函数使用 ERROR_INVALID_HANDLE 来指示对象本身不再有效。 例如,如果网络连接断开,尝试使用对网络上文件的句柄的函数可能会失败并ERROR_INVALID_HANDLE,因为文件对象不再可用。 在这种情况下,应用程序应关闭句柄。

如果处理句柄,则应在提交事务之前关闭绑定到事务的所有句柄。 如果使用 FILE_FLAG_DELETE_ON_CLOSE 标志调用 CreateFileTransacted 打开了事务处理句柄,则在应用程序关闭句柄并调用 CommitTransaction 之前,不会删除该文件。 有关事务处理对象的详细信息,请参阅 使用事务。

关闭线程句柄不会终止关联的线程或删除线程对象。 关闭进程句柄不会终止关联的进程或删除进程对象。 若要删除线程对象,必须终止该线程,然后关闭该线程的所有句柄。 有关详细信息,请参阅 终止线程。 若要删除进程对象,必须终止进程,然后关闭进程的所有句柄。 有关详细信息,请参阅 终止进程。

即使仍有文件视图处于打开状态,关闭文件映射的句柄也会成功。 有关详细信息,请参阅 关闭文件映射对象。

请勿使用 CloseHandle 函数关闭套接字。 请改用 closesocket 函数,该函数释放与套接字关联的所有资源,包括套接字对象的句柄。 有关详细信息,请参阅 套接字关闭。

请勿使用 CloseHandle 函数关闭打开的注册表项的句柄。 请改用 RegCloseKey 函数。 CloseHandle 不会关闭注册表项的句柄,但不返回指示此失败的错误。*/

老生常谈了,CloseHandle关闭句柄,这里不需要多解释


除了线程类的,我们还需要明白 事件

HANDLE CreateEvent(  
  LPSECURITY_ATTRIBUTES lpEventAttributes, // SD  
  BOOL bManualReset,                       // reset type  
  BOOL bInitialState,                      // initial state  
  LPCTSTR lpName                           // object name  
);  

/*lpEventAttributes:指向SECURITY_ATTRIBUTES结构体,此结构体决定函数的返回句柄是否可以让子进程继承。如果这个参数为NULL,这个句柄是不能继承的。一般情况下,这个参数设置为NULL。

bManualReset:指定将创建的EVENT是自动复位还是手动复位。如果为TRUE,需要用ResetEvent(HANDLE)函数手动复位状态为无信号,即一旦改EVENT被设置成有信号,则它会一直等到ResetEvent调用时才为无信号状态。如果为FALSE,当一个有信号的等待线程被释放后,系统会自动复位状态为无信号状态。

bInitialState:指定事件对象的初始状态。如果为TRUE,初始状态为有信号,否则为无信号。

lpName:  事件对象的名称,以字符串表示。名称的长度受MAX_PATH的限制,名称是大小写敏感的。如果lpName匹配一个存在的命名的事件对象,函数将请求EVENT_ALL_ACCESS来访问存在的对象。在这种情况下,bManualReset和bInitialState 被忽略,因为这两个参数已经被存在的事件设置。如果lpEventAttributes参数不为NULL,这个参数可以决定是否句柄被继承,但是它的安全描述(security-descriptor)成员被忽略。如果lpName 为NULL,创建一个没有名称的事件。如果lpName 匹配一个存在的semaphore, mutex, waitable timer, job或者file-mapping对象的名称,函数调用失败,GetLastError函数返回ERROR_INVALID_HANDLE。由于这些对象共享相同的命名空间,才导致这种情况的发生。

返回值:    函数返回句柄,该句柄具有EVENT_ALL_ACCESS权限去访问新的事件对象,同时它可以在任何需要事件对象句柄的函数中使用。

    调用过程中的任何线程,都可以在一个等待函数中指定事件对象句柄。当指定的对象的状态为有信号时,单对象等待函数(例如WaitForSingleObject)返回。对于多对象等待函数(例如WaitForMultipleObjects),可以指定为任意或所有指定的对象被置为有信号状态。当等待函数返回时,等待线程将被释放去继续它的执行。   事件对象的初始状态由bInitialState参数指定,用SetEvent函数可以设置对象为有信号状态,用ResetEvent函数可以设置对象为无信号状态。   当一个手动复原的事件对象的状态被置为有信号状态时,该对象将一直保持有信号状态,直至明确调用ResetEvent函数将其置为无符号状态。当事件对象被设置为有信号状态时,任何数量的等待线程或者随后等待的线程都会被释放。

    当一个自动复原事件对象的状态被设置为有信号状态时,该对象一直保持有信号状态,直至一个单等待线程被释放;系统然后会自动重置对象到无信号状态。   
多个进程可持有同一个事件对象的多个句柄,可以通过使用此对象来实现进程间的同步。下面的对象共享机制是可行的:

  ·在CreateEvent函数中,lpEventAttributes参数指定句柄可被继承时,通过CreateProcess函数创建的子进程继承的事件对象句柄。

  ·一个进程可以在DuplicateHandle函数中指定事件对象句柄,从而获得一个复制的句柄,此句柄可以被其它进程使用。

      ·一个进程可以在OpenEvent或CreateEvent函数中指定一个名字,从而获得一个有名的事件对象句柄。(在调用OpenEvent或CreateEvent函数时,一个进程可以指定事件对象的名字。)

  使用CloseHandle函数关闭句柄。当进程终止时,系统将自动关闭句柄。事件对象会被销毁,当最后一个句柄被关闭。

*/

创建一个事件

举例如下:

HANDLE MyEvent=CreateEvent(NULL, FALSE, TRUE, NULL);

接下来是对线程同步比较重要的两个函数

DWORD WaitForSingleObject( HANDLE hHandle, DWORDdwMilliseconds);
BOOL SetEvent(HANDLE hEvent);

回归一下正题,我们说完了所需要的函数,以及解释了意义

那么如何创建一个线程呢

#define BeginThread(Routine,Argument) CreateThread(0,0,(LPTHREAD_START_ROUTINE) Routine,Argument,0,0)

我们先定义了一个可以非常快捷使用的宏,怎么使用呢?

HANDLE hThread1=BeginThread(Function1, this);//在Class中使用
HANDLE hThread2=BeginThread(Function2, TRUE);//TRUE在普通的时候使用

void Function2(){
  printf("ShaShen Blog\n");
  Sleep(10000);//模拟耗时操作
}

内容大概就是这么多了


下个文章我也许会讲事件同步 在这里我只给出思路

1.假如说有3个线程,那就用CreateEvent创建三个事件hEvent1,hEvent2,hEvent3

2.创建的三个线程执行操作

3.主函数创建线程完成后及分别WaitForSingleObject分事件

4.三个线程执行完操作后,SetEvent分事件

5.主函数等到了所有事件,及同步完成,最后清理一下,删除hEvent1,hEvent2,hEvent3

这就是事件同步的思路了


希望本文章对您有用

Powered by ShaShen

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值