VC----Windows多线程
来源:互联网 发布:最新网络神曲 编辑:程序博客网 时间:2024/05/16 07:01
进程是系统中的重要概念,简单来说字面的意思就是一个运行中的程序,但是程序代表的是静态的指令代码。进程由系统管理的内核对象和存放程序运行资源的地址空间组成。内核对象由系统管理,因此应用程序是无法直接访问的;地址空间中则包含着程序运行所需的所有资源,如可执行模块、DLL、代码和数据,以及动态分配的栈与堆。可以说,其实进程就是程序运行的资源的容器。但是进程只是为程序的执行提供了一个场所,真正实现执行流程的是线程。每个进程启动时都会自动建立一个线程作为主线程,由它去创建其他的线程。线程与进程相比更为轻量级,二者的主要联系在于:
1. 线程创建在进程的地址空间中,对于进程资源有着完全访问权限,多线程间共享资源,可自由通信;
2. 线程建立时也有自己的内核对象和线程栈,而非地址空间;
3. 一般需要实现多任务时我们更推荐使用线程实现,因为创建一个进程需要分配地址空间,而且进程间的切换也很不方便,即使用进程开销很高;
一、创建多线程
简单了解了进程与线程的概念之后,我们来看看如何在程序中创建线程。Windows SDK为我们提供了创建线程的专用函数:CreateThread()
-1-第一个参数是安全属性结构,主要控制该线程句柄是否可为进程的子进程继承使用,默认使用NULL时表示不能继承;若想继承线程句柄,则需要设置该结构体,将结构体的bInheritHandle成员初始化为TRUE;
-2-cbStack表示的线程初始栈的大小,若使用0则表示采用默认大小初始化;
-3-lpStartAddr表示线程开始的位置,即线程要执行的函数代码,这点有点类似于回调函数的使用;
-4-lpvThreadParam用来接收线程过程函数的参数,不需要时可以设置为NULL;
-5-fdwCreate表示创建线程时的标志,CREATE_SUSPENDED表示线程创建后挂起暂不执行,必须调用ResumeThread才可以执行,0表示线程创建之后立即执行
-6-lpIDThread用来保存线程的ID;
了解了CreateThread函数的用法,我们来看一个例子实际体验一下。下面的例子会开启一个线程循环输出信息,我们可以查看结果:
由于我们使用system("pause")命令,因此我们不需要Sleep()命令同样可以看到主线程和分线程的交替执行:
二、多线程的问题
上面的程序我们只是建立一个新线程,如果建立两个呢?下面我们模拟一个火车售票的模型,线程1和线程2同时负责售票,主线程负责平台搭建。线程1和线程2分别访问全局变量tickets,输出其值作为票号然后将其值减一,直至“售完”所有票:
再次运行之后结果:
分析下上面的结果,首先线程1和线程2确实交替运行“购票”了,其次看到不规则的输出,1314连到一起。关于这点我们要知道由于CPU是执行分片处理的,即不同的线程会得到不同的时间片来执行程序,尤其是对于单CPU来说更是如此,因此当线程1执行到"Thread 1 sell ticket"时结束分片,由线程2继续执行;线程2执行到同样位置再次交还给线程1继续执行显示“13”,然后线程2执行显示“14”。这样看好像也没有问题,但是问题在于最后出现了“0”,我们的代码中是不允许出现0的,之所以这样,同上面的元婴一样,在线程1执行到ticket = 1时交接给了线程2执行,并且输出了其值1,然后线程1继续输出了当前值0.此时的if条件就失去作用了。虽然这种情况不一定总是发生,但是在实际的操作中是很有可能出现的,而且排查起来也很困难。那么我们该如何解决呢?
问题的关键在于线程1和线程2同时访问了全局变量tickets,导致了错误;那么我们的解决方案就是将全局变量的访问控制起来就可以了,不允许同时有多个线程同时访问该变量。我们使用互斥量来实现。
三、互斥量的应用
使用互斥量并不困难,核心步骤如下:
-1-CreateMutex创建一个互斥量:
第一个参数同样是安全结构,默认是NULL不能继承句柄;第二个参数为FALSE时创建Mutex时不指定所有权,若为TRUE则指定为当前的创建线程ID为所有者,其他线程访问需要先ReleaseMutex;第三个参数用于设置Mutex名,后续我们会说明,为NULL时表示是匿名互斥量。
-2-WaitForSingleObject():请求一个互斥量的访问权;
-3-ReleaseMutex():释放一个互斥量的访问权;
好了,我们再来看看应用了互斥量的改进程序:
这次运行之后的结果不会出现上面的错误了:
互斥量还有一个小应用,利用命名互斥量来保证只有一个程序实例运行。我们可以创建一个命名互斥量,当程序要重复运行时,检查互斥量的返回值,若为ERROR_ALREADY_EXISTS则表示已经有一个实例运行了,直接return即可。在源程序中添加以下代码:
1. 线程创建在进程的地址空间中,对于进程资源有着完全访问权限,多线程间共享资源,可自由通信;
2. 线程建立时也有自己的内核对象和线程栈,而非地址空间;
3. 一般需要实现多任务时我们更推荐使用线程实现,因为创建一个进程需要分配地址空间,而且进程间的切换也很不方便,即使用进程开销很高;
一、创建多线程
简单了解了进程与线程的概念之后,我们来看看如何在程序中创建线程。Windows SDK为我们提供了创建线程的专用函数:CreateThread()
点击(此处)折叠或打开
- HANDLE CreateThread(
- LPSECURITY_ATTRIBUTES lpsa,
- DWORD cbStack,
- LPTHREAD_START_ROUTINE lpStartAddr,
- LPVOID lpvThreadParam,
- DWORD fdwCreate,
- LPDWORD lpIDThread
- );
-2-cbStack表示的线程初始栈的大小,若使用0则表示采用默认大小初始化;
-3-lpStartAddr表示线程开始的位置,即线程要执行的函数代码,这点有点类似于回调函数的使用;
-4-lpvThreadParam用来接收线程过程函数的参数,不需要时可以设置为NULL;
-5-fdwCreate表示创建线程时的标志,CREATE_SUSPENDED表示线程创建后挂起暂不执行,必须调用ResumeThread才可以执行,0表示线程创建之后立即执行
-6-lpIDThread用来保存线程的ID;
了解了CreateThread函数的用法,我们来看一个例子实际体验一下。下面的例子会开启一个线程循环输出信息,我们可以查看结果:
点击(此处)折叠或打开
- //MultiThread
- #include <iostream>
- #include <cstdlib>
- #include <windows.h>
- using namespace std;
- DWORD WINAPI Fun1Proc(LPVOID lpParameter);
- int main()
- {
- int j = 0;
-
- HANDLE hThread_1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
- CloseHandle(hThread_1);
-
- while (j++ < 1000)
- cout << "MainThread is running for" << " the "<< j <<" times "<<endl;
-
- system("pause");
- return 0;
-
-
- }
- DWORD WINAPI Fun1Proc(LPVOID lpParameter)
- {
- int i = 0;
- while (i++ < 1000)
- cout << "Thread 1 is running for" <<" the "<< i <<" times "<<endl;
-
- return 0;
- }
二、多线程的问题
上面的程序我们只是建立一个新线程,如果建立两个呢?下面我们模拟一个火车售票的模型,线程1和线程2同时负责售票,主线程负责平台搭建。线程1和线程2分别访问全局变量tickets,输出其值作为票号然后将其值减一,直至“售完”所有票:
点击(此处)折叠或打开
- //MultiThread
- #include <iostream>
- #include <cstdlib>
- #include <windows.h>
- using namespace std;
- DWORD WINAPI Fun1Proc(LPVOID lpParameter);
- DWORD WINAPI Fun2Proc(LPVOID lpParameter);
- int tickets = 100;
- int main()
- {
- HANDLE hThread_1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
- HANDLE hThread_2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
- CloseHandle(hThread_1);
- CloseHandle(hThread_2);
- system("pause");
- return 0;
-
-
- }
- DWORD WINAPI Fun1Proc(LPVOID lpParameter)
- {
- while (true)
- {
- if (tickets > 0)
- {
- Sleep(10);
- cout << "Thread 1 sell ticket : "<<tickets--<<endl;
- }
- else
- break;
- }
- return 0;
- }
-
- DWORD WINAPI Fun2Proc(LPVOID lpParameter)
- {
- while (true)
- {
-
- if (tickets > 0)
- {
- Sleep(10);
- cout << "Thread 2 sell ticket : "<<tickets--<<endl;
- }
- else
- break;
-
- }
- return 0;
- }
分析下上面的结果,首先线程1和线程2确实交替运行“购票”了,其次看到不规则的输出,1314连到一起。关于这点我们要知道由于CPU是执行分片处理的,即不同的线程会得到不同的时间片来执行程序,尤其是对于单CPU来说更是如此,因此当线程1执行到"Thread 1 sell ticket"时结束分片,由线程2继续执行;线程2执行到同样位置再次交还给线程1继续执行显示“13”,然后线程2执行显示“14”。这样看好像也没有问题,但是问题在于最后出现了“0”,我们的代码中是不允许出现0的,之所以这样,同上面的元婴一样,在线程1执行到ticket = 1时交接给了线程2执行,并且输出了其值1,然后线程1继续输出了当前值0.此时的if条件就失去作用了。虽然这种情况不一定总是发生,但是在实际的操作中是很有可能出现的,而且排查起来也很困难。那么我们该如何解决呢?
问题的关键在于线程1和线程2同时访问了全局变量tickets,导致了错误;那么我们的解决方案就是将全局变量的访问控制起来就可以了,不允许同时有多个线程同时访问该变量。我们使用互斥量来实现。
三、互斥量的应用
使用互斥量并不困难,核心步骤如下:
-1-CreateMutex创建一个互斥量:
点击(此处)折叠或打开
- HANDLE CreateMutex(
- LPSECURITY_ATTRIBUTES lpMutexAttributes,
- BOOL bInitialOwner,
- LPCTSTR lpName
- );
-2-WaitForSingleObject():请求一个互斥量的访问权;
-3-ReleaseMutex():释放一个互斥量的访问权;
好了,我们再来看看应用了互斥量的改进程序:
点击(此处)折叠或打开
- //MultiThread
- #include <iostream>
- #include <cstdlib>
- #include <windows.h>
- using namespace std;
- DWORD WINAPI Fun1Proc(LPVOID lpParameter);
- DWORD WINAPI Fun2Proc(LPVOID lpParameter);
- int tickets = 100;
- HANDLE hMutex;
- int main()
- {
- hMutex = CreateMutex(NULL, FALSE, NULL);
-
- HANDLE hThread_1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
- HANDLE hThread_2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
- CloseHandle(hThread_1);
- CloseHandle(hThread_2);
-
- system("pause");
- return 0;
-
-
- }
- DWORD WINAPI Fun1Proc(LPVOID lpParameter)
- {
- while (true)
- {
- WaitForSingleObject(hMutex, INFINITE);
- if (tickets > 0)
- {
- Sleep(10);
- cout << "Thread 1 sell ticket : "<<tickets--<<endl;
- }
- else
- break;
- ReleaseMutex(hMutex);
- }
- return 0;
- }
-
- DWORD WINAPI Fun2Proc(LPVOID lpParameter)
- {
- while (true)
- {
- WaitForSingleObject(hMutex, INFINITE);
- if (tickets > 0)
- {
- Sleep(10);
- cout << "Thread 2 sell ticket : "<<tickets--<<endl;
- }
- else
- break;
- ReleaseMutex(hMutex);
- }
- return 0;
- }
互斥量还有一个小应用,利用命名互斥量来保证只有一个程序实例运行。我们可以创建一个命名互斥量,当程序要重复运行时,检查互斥量的返回值,若为ERROR_ALREADY_EXISTS则表示已经有一个实例运行了,直接return即可。在源程序中添加以下代码:
点击(此处)折叠或打开
- //确保只有一个实例运行
- HANDLE hMutex_1 = CreateMutex(NULL, TRUE, "tickets");
- if (hMutex_1)
- {
- if (ERROR_ALREADY_EXISTS == GetLastError())
- {
- cout << "Only one instance can run !" << endl;
- system("pause");
- return 0;
- }
- }
0 0
- windows vc多线程
- VC----Windows多线程
- windows多线程(sunxin vc++ 笔记2)
- VC windows api 多线程---互斥量、信号量、临界值
- VC windows api 多线程---临界区
- Qt中使用windows socket api vc多线程socket
- 在windows下VC中编译多线程需要如下设置
- VC多线程
- VC 多线程
- vc++多线程
- VC多线程
- vc 多线程
- VC多线程
- vc 多线程
- VC多线程
- vc多线程
- VC 多线程
- VC多线程
- Mybatis 查不到数据,总是返回Null
- 李开复给大学生的第3封信:成功、自信、快乐
- 奇偶节点的排序和完全平方数问题
- Android开发之多线程编程Thread和Runnable使用
- HDU 1166 | 敌兵布阵 —— 树状数组
- VC----Windows多线程
- (四十三)、线程的同步和线程池
- UVALive 6811 Irrigation Lines (二分图最小点覆盖--匈牙利算法)
- GitHub上传代码
- iOS第一次加载键盘速度慢
- 编码及常见的编码方式
- 关于log4j2在windows和ubuntu上的部分使用经验
- MySQL数据库的增删改查
- VC----小结