【线程】
创建线程的函数是CreateThread,其原型如下:
HANDLE WINAPI CreateThread
(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
参数说明:
在实际coding中,根据线程的作用,可以把线程封装在一个类里,如果需要让线程唯一,那么线程函数应该为static的;如果想让线程不唯一,那么就需要以成员函数的形式传入。不论上述两种情况的哪一种,都可以让第四个参数传入this,这样做比较方便。
如果线程能够返回,就可以确保下列事项的实现:
ExitThread函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被撤消。
TerminateThread函数终止线程时,会造成如下结果:不会对关键代码段对象进行操作,也就是说,如果线程中存在关键代码段,那他不会被销毁。分配的资源也不会从堆中撤销。线程状态被设置成inconsistent。当线程终止运行时,DLL通常接收通知。但使用TerminateThread 强迫线程终止,DLL就不接收通知。
【关键代码段】
CRITICAL_SECTION 变量被用于控制多线程访问共享区的互斥行为。
在使用关键代码段之前,要对 CRITICAL_SECTION进行初始化,调用Initialize函数,需要使用互斥时调用Enter函数,结束互斥行为时使用Leave函数,销毁变量时使用Delete函数,原型如下:
void
InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
void
EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
void
LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
void
DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
EnterCriticalSection
函数负责查看该结构中的成员变量。这些变量用于指明当前是哪个变量正在访问该资源。如果成员变量指明,调用线程已经被赋予对资源的访问权,那么
EnterCriticalSection
便更新这些变量,以指明调用线程多少次被赋予访问权并立即返回,使该线程能够继续运行。
如果成员变量指明,一个线程(除了调用线程之外)已被赋予对资源的访问权,那么 EnterCriticalSection 将调用线程置于等待状态。这种等待的线程不会浪费任何CPU时间。系统能够记住该线程想要访问该资源并且自动更新 CRITICAL_SECTION 的成员变量,一旦目前访问该资源的线程调用 LeaveCriticalSection 函数,该线程就处于可调度状态。
LeaveCriticalSection 要查看该结构中的成员变量。该函数每次计数时要递减1,以指明调用线程多少次被赋予对共享资源的访问权。如果该计数大于0,那么 LeaveCriticalSection 不做其他
任何操作,只是返回而已。如果该计数变为0,它就要查看在调用 EnterCriticalSection 中是否有别的线程正在等待。如果至少有一个线程正在等待,它就更新成员变量,并使等待线程中的一个线程(“公正地”选定)再次处于可调度状态。如果没有线程正在等待, LeaveCriticalSection 函数就更新成员变量,以指明没有线程正在访问该资源。
如果不想等,想得到要么能够使用资源,要么去做别的事情的方法,就用Try EnterCriticalSection,当能够使用资源时,该函数返回TRUE,不能时返回FALSE。他不会把自己加入等待队列中去。
使用关键代码段时有一些技巧:
创建线程的函数是CreateThread,其原型如下:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
- 第一个参数关于安全性,通常值为NULL。Windows会设置为默认安全级别。
- 第二个参数关于堆栈大小,(关于线程的堆栈,以后有机会一定仔细学习)对于简单应用,同安全性相同,使用默认的大小即可,一般传入0。
- 第三个参数是线程函数名,用于指明想要新线程执行的线程函数的地址。线程函数可以使用任何名字。实际上,如果在应用程序中拥有多个线程函数,必须为它们赋予不同的名字,否则编译器/链接程序会认为你为单个函数创建了多个实现函数。
- 第四个参数是线程函数的传入参数,在线程启动执行时将该参数传递给线程函数。该参数提供了一个将初始化值传递给线程函数的手段。该初始化数据既可以是数字值,也可以是指向包含其他信息的一个数据结构的指针。
- 第五个参数是设定用于控制创建线程的其他标志。它可以是两个值中的一个。如果该值是0,那么线程创建后可以立即进行调度。如果该值是CREATE_SUSPENDED,系统可以完整地创建线程并对它进行初始化,但是要暂停该线程的运行,这样它就无法进行调度。
- 第六个参数是线程的ID,当传入NULL时,表示并不关心线程的ID。
- 返回值:线程函数必须返回一个值,它将成为该线程的退出代码。
在实际coding中,根据线程的作用,可以把线程封装在一个类里,如果需要让线程唯一,那么线程函数应该为static的;如果想让线程不唯一,那么就需要以成员函数的形式传入。不论上述两种情况的哪一种,都可以让第四个参数传入this,这样做比较方便。
如果线程能够返回,就可以确保下列事项的实现:
- 在线程函数中创建的所有C + +对象均将通过它们的撤消函数正确地撤消。
- 操作系统将正确地释放线程堆栈使用的内存。
- 系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
- 系统将递减线程内核对象的使用计数。
ExitThread函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被撤消。
TerminateThread函数终止线程时,会造成如下结果:不会对关键代码段对象进行操作,也就是说,如果线程中存在关键代码段,那他不会被销毁。分配的资源也不会从堆中撤销。线程状态被设置成inconsistent。当线程终止运行时,DLL通常接收通知。但使用TerminateThread 强迫线程终止,DLL就不接收通知。
【关键代码段】
CRITICAL_SECTION 变量被用于控制多线程访问共享区的互斥行为。
在使用关键代码段之前,要对 CRITICAL_SECTION进行初始化,调用Initialize函数,需要使用互斥时调用Enter函数,结束互斥行为时使用Leave函数,销毁变量时使用Delete函数,原型如下:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
如果成员变量指明,一个线程(除了调用线程之外)已被赋予对资源的访问权,那么 EnterCriticalSection 将调用线程置于等待状态。这种等待的线程不会浪费任何CPU时间。系统能够记住该线程想要访问该资源并且自动更新 CRITICAL_SECTION 的成员变量,一旦目前访问该资源的线程调用 LeaveCriticalSection 函数,该线程就处于可调度状态。
LeaveCriticalSection 要查看该结构中的成员变量。该函数每次计数时要递减1,以指明调用线程多少次被赋予对共享资源的访问权。如果该计数大于0,那么 LeaveCriticalSection 不做其他
任何操作,只是返回而已。如果该计数变为0,它就要查看在调用 EnterCriticalSection 中是否有别的线程正在等待。如果至少有一个线程正在等待,它就更新成员变量,并使等待线程中的一个线程(“公正地”选定)再次处于可调度状态。如果没有线程正在等待, LeaveCriticalSection 函数就更新成员变量,以指明没有线程正在访问该资源。
如果不想等,想得到要么能够使用资源,要么去做别的事情的方法,就用Try EnterCriticalSection,当能够使用资源时,该函数返回TRUE,不能时返回FALSE。他不会把自己加入等待队列中去。
使用关键代码段时有一些技巧:
- 每个共享资源使用一个CRITICAL_SECTION变量,如果应用程序中拥有若干个互不相干的数据结构,应该为每个数据结构创建一个CRITICAL_SECTION变量。这比只有单个CRITICAL_SECTION结构来保护对所有共享资源的访问要好(效率高)。
- 当同时访问多个资源时,必须始终按照完全相同的顺序请求对资源的访问。
- 不要长时间运行关键代码段,当一个关键代码段长时间运行时,其他线程就会进入等待状态,这会降低应用程序的运行性能。