C++ 多线程 CreateThread函数使用
#include <stdlib.h>
#include <windows.h>
//头文件引用较多, 有一些与本程序无关
/*
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全相关的属性,常置为NULL
SIZE_T dwStackSize, //新线程的初始化栈在大小,可设置为0
LPTHREAD_START_ROUTINE lpStartAddress, //被线程执行的回调函数,也称为线程函数
LPVOID lpParameter, //传入线程函数的参数,不需传递参数时为NULL
DWORD dwCreationFlags, //控制线程创建的标志
LPDWORD lpThreadId //传出参数,用于获得线程ID,如果为NULL则不返回线程ID
);
*/
using namespace std;
volatile int b = 0;
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int i = 10000;
int *p = (int*)lpParameter;
while(i--)
{
(*p)++;
b++;
}
return 0;
}
int main(int argc, char* argv[])
{
int a = 0;
HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
HANDLE hThread3 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
HANDLE hThread4 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
HANDLE hThread5 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
Sleep(1000);
CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hThread3);
CloseHandle(hThread4);
CloseHandle(hThread5);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
system("pause");
return 0;
}
多线程之使用Mutex和Critical_Section互斥变量访问共享变量
Mutex和Critical Section都是主要用于限制多线程(Multithread)对全局或共享的变量、对象或内存空间的访问。下面是其主要的异同点(不同的地方用绿色表示)。
Mutex | Critical Section | |
性能和速度 | 慢。 Mutex 是内核对象,相关函数的执行 (WaitForSingleObject, ReleaseMutex)需要用户模式(User Mode)到内核模式 (Kernel Mode)的转换,在x86处理器上这种转化一般要 发费600个左右的 CPU指令周期。 | 快。 Critical Section本身不是内核对象,相关函数 (EnterCriticalSection,LeaveCriticalSection) 的调用一般都在用户模式内执行,在x86处理器上 一般只需要发费9个左右的 CPU指令周期。只有 当想要获得的锁正好被别的线程拥有时才会退化 成和Mutex一样,即转换到内核模式,发费600个 左右的 CPU指令周期。 |
能否跨越进程(Process)边界 | 可以 | 不可 |
定义写法 | HANDLE hmtx; | CRITICAL_SECTION cs; |
初始化写法 | hmtx= CreateMutex (NULL, FALSE, NULL); | InitializeCriticalSection(&cs); |
结束清除写法 | CloseHandle(hmtx); | DeleteCriticalSection(&cs); |
无限期等待的写法 | WaitForSingleObject (hmtx, INFINITE); | EnterCriticalSection(&cs); |
0等待(状态检测)的写法 | WaitForSingleObject (hmtx, 0); | TryEnterCriticalSection(&cs); |
任意时间等待的写法 | WaitForSingleObject (hmtx, dwMilliseconds); | 不支持 |
锁释放的写法 | ReleaseMutex(hmtx); | LeaveCriticalSection(&cs); |
能否被一道用于等待其他内核对象 | 可以(使用WaitForMultipleObjects, WaitForMultipleObjectsEx, MsgWaitForMultipleObjects, MsgWaitForMultipleObjectsEx等等) | 不可 |
当拥有锁的线程死亡时 | Mutex变成abandoned状态,其他的等待线程可以获得锁。 | Critical Section的状态不可知(undefined), 以后的动作就不能保证了。 |
自己会不会锁住自己 | 不会(对已获得的Mutex,重复调用WaitForSingleObject不会 锁住自己。但最后你别忘了要调用同样次数的 ReleaseMutex) | 不会(对已获得的Critical Section,重复调用 EnterCriticalSection不会锁住自己。但最后 你别忘了要调用同样次数的 LeaveCriticalSection) |
下面是一些补充:
l 请先检查你的设计,把不必要的全局或共享对象改为局部对象。全局的东西越少,出问题的可能就越小。
l 每次你使用EnterCriticalSection时,请不要忘了在函数的所有可能返回的地方都加上LeaveCriticalSection。对于Mutex也同样。若你把这个问题和Win32 structured exception或C++ exception一起考虑,你会发现问题并不是那么简单。自定义一个封装类可能是一种解决方案,以Critical Section为例的代码如下所示:
class csholder
{
CRITICAL_SECTION *cs;
public:
csholder(CRITICAL_SECTION *c): cs(c)
{ EnterCriticalSection(cs); }
~csholder() { LeaveCriticalSection(cs); }
};
CRITICAL_SECTION some_cs;
void foo()
{
// ...
csholder hold_some(&some_cs);
// ... CS protected code here
// at return or if an exception happens
// hold_some's destructor is automatically called
}
l 根据你的互斥范围需求的不同,把Mutex或Critical Section定义为类的成员变量,或者静态类变量。
l 若你想限制访问的全局变量只有一个而且类型比较简单(比如是LONG或PVOID型),你也可以使用InterlockedXXX系列函数来保证一个线程写多个线程读。
Demo程序
#include <Windows.h>
#include <iostream>
using namespace std;
int index = 0;
// 临界区结构对象
CRITICAL_SECTION g_cs;
HANDLE hMutex = NULL;
void changeMe()
{
cout << index++ << endl;
}
void changeMe2()
{
cout << index++ << endl;
}
void changeMe3()
{
cout << index++ << endl;
}
DWORD WINAPI th1(LPVOID lpParameter)
{
while(1)
{
Sleep(1600); //sleep 1.6 s
// 进入临界区
EnterCriticalSection(&g_cs);
// 等待互斥对象通知
//WaitForSingleObject(hMutex, INFINITE);
// 对共享资源进行写入操作
//cout << "a" << index++ << endl;
changeMe();
changeMe2();
changeMe3();
// 释放互斥对象
//ReleaseMutex(hMutex);
// 离开临界区
LeaveCriticalSection(&g_cs);
}
return 0;
}
DWORD WINAPI th2(LPVOID lpParameter)
{
while(1)
{
Sleep(2000); //sleep 2 s
// 进入临界区
EnterCriticalSection(&g_cs);
// 等待互斥对象通知
//WaitForSingleObject(hMutex, INFINITE);
//cout << "b" << index++ << endl;
changeMe();
changeMe2();
changeMe3();
// 释放互斥对象
//ReleaseMutex(hMutex);
// 离开临界区
LeaveCriticalSection(&g_cs);
}
return 0;
}
int main(int argc, char* argv[])
{
// 创建互斥对象
//hMutex = CreateMutex(NULL, TRUE, NULL);
// 初始化临界区
InitializeCriticalSection(&g_cs);
HANDLE hThread1;
HANDLE hThread2;
hThread1 = CreateThread(NULL, 0, th1, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, th2, NULL, 0, NULL);
int k;
cin >> k;
printf("Hello World!\n");
return 0;
}
Mutex | Critical Section | |
性能和速度 | 慢。 Mutex 是内核对象,相关函数的执行 (WaitForSingleObject, ReleaseMutex)需要用户模式(User Mode)到内核模式 (Kernel Mode)的转换,在x86处理器上这种转化一般要 发费600个左右的 CPU指令周期。 | 快。 Critical Section本身不是内核对象,相关函数 (EnterCriticalSection,LeaveCriticalSection) 的调用一般都在用户模式内执行,在x86处理器上 一般只需要发费9个左右的 CPU指令周期。只有 当想要获得的锁正好被别的线程拥有时才会退化 成和Mutex一样,即转换到内核模式,发费600个 左右的 CPU指令周期。 |
能否跨越进程(Process)边界 | 可以 | 不可 |
定义写法 | HANDLE hmtx; | CRITICAL_SECTION cs; |
初始化写法 | hmtx= CreateMutex (NULL, FALSE, NULL); | InitializeCriticalSection(&cs); |
结束清除写法 | CloseHandle(hmtx); | DeleteCriticalSection(&cs); |
无限期等待的写法 | WaitForSingleObject (hmtx, INFINITE); | EnterCriticalSection(&cs); |
0等待(状态检测)的写法 | WaitForSingleObject (hmtx, 0); | TryEnterCriticalSection(&cs); |
任意时间等待的写法 | WaitForSingleObject (hmtx, dwMilliseconds); | 不支持 |
锁释放的写法 | ReleaseMutex(hmtx); | LeaveCriticalSection(&cs); |
能否被一道用于等待其他内核对象 | 可以(使用WaitForMultipleObjects, WaitForMultipleObjectsEx, MsgWaitForMultipleObjects, MsgWaitForMultipleObjectsEx等等) | 不可 |
当拥有锁的线程死亡时 | Mutex变成abandoned状态,其他的等待线程可以获得锁。 | Critical Section的状态不可知(undefined), 以后的动作就不能保证了。 |
自己会不会锁住自己 | 不会(对已获得的Mutex,重复调用WaitForSingleObject不会 锁住自己。但最后你别忘了要调用同样次数的 ReleaseMutex) | 不会(对已获得的Critical Section,重复调用 EnterCriticalSection不会锁住自己。但最后 你别忘了要调用同样次数的 LeaveCriticalSection) |
Mutex | Critical Section | |
性能和速度 | 慢。 Mutex 是内核对象,相关函数的执行 (WaitForSingleObject, ReleaseMutex)需要用户模式(User Mode)到内核模式 (Kernel Mode)的转换,在x86处理器上这种转化一般要 发费600个左右的 CPU指令周期。 | 快。 Critical Section本身不是内核对象,相关函数 (EnterCriticalSection,LeaveCriticalSection) 的调用一般都在用户模式内执行,在x86处理器上 一般只需要发费9个左右的 CPU指令周期。只有 当想要获得的锁正好被别的线程拥有时才会退化 成和Mutex一样,即转换到内核模式,发费600个 左右的 CPU指令周期。 |
能否跨越进程(Process)边界 | 可以 | 不可 |
定义写法 | HANDLE hmtx; | CRITICAL_SECTION cs; |
初始化写法 | hmtx= CreateMutex (NULL, FALSE, NULL); | InitializeCriticalSection(&cs); |
结束清除写法 | CloseHandle(hmtx); | DeleteCriticalSection(&cs); |
无限期等待的写法 | WaitForSingleObject (hmtx, INFINITE); | EnterCriticalSection(&cs); |
0等待(状态检测)的写法 | WaitForSingleObject (hmtx, 0); | TryEnterCriticalSection(&cs); |
任意时间等待的写法 | WaitForSingleObject (hmtx, dwMilliseconds); | 不支持 |
锁释放的写法 | ReleaseMutex(hmtx); | LeaveCriticalSection(&cs); |
能否被一道用于等待其他内核对象 | 可以(使用WaitForMultipleObjects, WaitForMultipleObjectsEx, MsgWaitForMultipleObjects, MsgWaitForMultipleObjectsEx等等) | 不可 |
当拥有锁的线程死亡时 | Mutex变成abandoned状态,其他的等待线程可以获得锁。 | Critical Section的状态不可知(undefined), 以后的动作就不能保证了。 |
自己会不会锁住自己 | 不会(对已获得的Mutex,重复调用WaitForSingleObject不会 锁住自己。但最后你别忘了要调用同样次数的 ReleaseMutex) | 不会(对已获得的Critical Section,重复调用 EnterCriticalSection不会锁住自己。但最后 你别忘了要调用同样次数的 LeaveCriticalSection) |
Mutex | Critical Section | |
性能和速度 | 慢。 Mutex 是内核对象,相关函数的执行 (WaitForSingleObject, ReleaseMutex)需要用户模式(User Mode)到内核模式 (Kernel Mode)的转换,在x86处理器上这种转化一般要 发费600个左右的 CPU指令周期。 | 快。 Critical Section本身不是内核对象,相关函数 (EnterCriticalSection,LeaveCriticalSection) 的调用一般都在用户模式内执行,在x86处理器上 一般只需要发费9个左右的 CPU指令周期。只有 当想要获得的锁正好被别的线程拥有时才会退化 成和Mutex一样,即转换到内核模式,发费600个 左右的 CPU指令周期。 |
能否跨越进程(Process)边界 | 可以 | 不可 |
定义写法 | HANDLE hmtx; | CRITICAL_SECTION cs; |
初始化写法 | hmtx= CreateMutex (NULL, FALSE, NULL); | InitializeCriticalSection(&cs); |
结束清除写法 | CloseHandle(hmtx); | DeleteCriticalSection(&cs); |
无限期等待的写法 | WaitForSingleObject (hmtx, INFINITE); | EnterCriticalSection(&cs); |
0等待(状态检测)的写法 | WaitForSingleObject (hmtx, 0); | TryEnterCriticalSection(&cs); |
任意时间等待的写法 | WaitForSingleObject (hmtx, dwMilliseconds); | 不支持 |
锁释放的写法 | ReleaseMutex(hmtx); | LeaveCriticalSection(&cs); |
能否被一道用于等待其他内核对象 | 可以(使用WaitForMultipleObjects, WaitForMultipleObjectsEx, MsgWaitForMultipleObjects, MsgWaitForMultipleObjectsEx等等) | 不可 |
当拥有锁的线程死亡时 | Mutex变成abandoned状态,其他的等待线程可以获得锁。 | Critical Section的状态不可知(undefined), 以后的动作就不能保证了。 |
自己会不会锁住自己 | 不会(对已获得的Mutex,重复调用WaitForSingleObject不会 锁住自己。但最后你别忘了要调用同样次数的 ReleaseMutex) | 不会(对已获得的Critical Section,重复调用 EnterCriticalSection不会锁住自己。但最后 你别忘了要调用同样次数的 LeaveCriticalSection) |