多线程(二)基本使用方法

编写一个多线程Demo用于演示多线程基本操作

功能介绍:

1. 有三个线程,线程1 线程2 线程3,分别在间隔时间0.5秒,1秒,1.5秒绘制一个椭圆形,椭圆形数量为10时线程自动退出

2. 添加三个静态文本用于显示三个线程绘制的椭圆形数量. 默认为0

3. 添加四个按钮,一个用于启动线程,一个用于挂起/恢复线程,一个用于通知线程结束,一个用于强制终止线程

最终效果演示:

 

 

开始编写代码:

1. 创建个基于对话框的工程MultiDemo

2. 添加三个静态文本

修改ID为:IDC_STATIC_THREAD_1  IDC_STATIC_THREAD_2  IDC_STATIC_THREAD_3

3.创建四个按钮

按钮1标题为“启动线程”,ID:IDC_BTN_THREAD_RUN 

按钮2标题为“挂起线程”,ID:IDC_BTN_THREAD_SUSPEND_RESUME

按钮3标题为“通知线程结束”,ID:IDC_BTN_THREAD_NOTIFYEXIT

按钮1标题为“强制终止线程”,ID:IDC_BTN_THREAD_TERMINATE

界面如下:

 

 开始编写具体代码

4. 先定义相关全局变量和线程函数前置声明

 1 int g_nNumOne = 0; //线程1绘制椭圆数量
 2 int g_nNumTwo = 0; //线程2绘制椭圆数量
 3 int g_nNumThree = 0;//线程3绘制椭圆数量
 4 
 5 HANDLE g_hThreadOne = 0; //线程1句柄
 6 HANDLE g_hThreadTwo = 0; //线程2句柄
 7 HANDLE g_hThreadThree = 0; //线程3句柄
 8 
 9 HWND g_MainWnd = 0; //主窗口句柄
10 bool g_bThreadExit = false; //用于线程判断自身是否需要退出
11 
12 RECT g_rectEllipseOne; //第一个椭圆矩形区域
13 RECT g_rectEllipseTwo; //第二个椭圆矩形区域
14 RECT g_rectEllipseThree; //第三个椭圆矩形区域
15 
16 //线程函数前置声明
17 DWORD WINAPI ThreadOne(LPVOID lpParam);
18 DWORD WINAPI ThreadTwo(LPVOID lpParam);
19 DWORD WINAPI ThreadThree(LPVOID lpParam);

5.然后定义三个线程函数

 1 //线程1
 2 DWORD WINAPI ThreadOne(LPVOID lpParam)
 3 {
 4     //获得DC
 5     HDC hdc = GetDC(g_MainWnd);
 6     //创建一个红色的画刷并选到DC里面去
 7     HBRUSH hBr = CreateSolidBrush(RGB(255,0,0));
 8     HBRUSH hOldBr = static_cast<HBRUSH>(SelectObject(hdc,hBr));
 9     TCHAR szText[4] = {0};
10     //判断下线程是否需要退出,以及是否达到绘制椭圆最大数量
11     while (!g_bThreadExit && g_nNumOne<10)
12     {
13         //每隔0.5秒先设置静态文框显示绘制椭圆的数量,然后绘制椭圆
14         Sleep(500);
15         g_nNumOne++;
16         _itot_s(g_nNumOne,szText,4,10);
17         SetDlgItemText(g_MainWnd,IDC_STATIC_THREAD_1,szText);
18         Ellipse(hdc,g_rectEllipseOne.left,g_rectEllipseOne.top-g_nNumOne*10,
19             g_rectEllipseOne.right,g_rectEllipseOne.bottom-g_nNumOne*10);
20     }
21 
22     //把旧的画刷选回去,并删除新的画刷
23     SelectObject(hdc,hOldBr);
24     DeleteObject(hBr);
25     //释放DC
26     ReleaseDC(g_MainWnd,hdc);
27     //关闭线程句柄
28     CloseHandle(g_hThreadOne);
29     g_hThreadOne = 0;
30     OutputDebugStringA("ThreadOne is Exit\r\n");
31     return TRUE;
32 }
33 //线程2
34 DWORD WINAPI ThreadTwo(LPVOID lpParam)
35 {
36     HDC hdc = GetDC(g_MainWnd);
37     //创建一个绿色的画刷并选到DC里面去
38     HBRUSH hBr = CreateSolidBrush(RGB(0,255,0));
39     HBRUSH hOldBr = static_cast<HBRUSH>(SelectObject(hdc,hBr));
40     TCHAR szText[4] = {0};
41 
42     while (!g_bThreadExit && g_nNumTwo<10)
43     {
44         Sleep(1000);
45         g_nNumTwo++;
46         _itot_s(g_nNumTwo,szText,4,10);
47         SetDlgItemText(g_MainWnd,IDC_STATIC_THREAD_2,szText);
48         Ellipse(hdc,g_rectEllipseTwo.left,g_rectEllipseTwo.top-g_nNumTwo*10,
49             g_rectEllipseTwo.right,g_rectEllipseTwo.bottom-g_nNumTwo*10);
50     }
51 
52     SelectObject(hdc,hOldBr);
53     DeleteObject(hBr);
54     ReleaseDC(g_MainWnd,hdc);
55     CloseHandle(g_hThreadTwo);
56     g_hThreadTwo = 0;
57     OutputDebugStringA("ThreadTwo is Exit\r\n");
58     return TRUE;
59 }
60 //线程3
61 DWORD WINAPI ThreadThree(LPVOID lpParam)
62 {
63     HDC hdc = GetDC(g_MainWnd);
64     //创建一个蓝色的画刷并选到DC里面去
65     HBRUSH hBr = CreateSolidBrush(RGB(0,0,255));
66     HBRUSH hOldBr = static_cast<HBRUSH>(SelectObject(hdc,hBr));
67     TCHAR szText[4] = {0};
68 
69     while (!g_bThreadExit && g_nNumThree<10)
70     {
71         Sleep(1500);
72         g_nNumThree++;
73         _itot_s(g_nNumThree,szText,4,10);
74         SetDlgItemText(g_MainWnd,IDC_STATIC_THREAD_3,szText);
75         Ellipse(hdc,g_rectEllipseThree.left,g_rectEllipseThree.top-g_nNumThree*10,
76             g_rectEllipseThree.right,g_rectEllipseThree.bottom-g_nNumThree*10);
77     }
78 
79     SelectObject(hdc,hOldBr);
80     DeleteObject(hBr);
81     ReleaseDC(g_MainWnd,hdc);
82     CloseHandle(g_hThreadThree);
83     g_hThreadThree = 0;
84     OutputDebugStringA("ThreadThree is Exit\r\n");
85     return TRUE;
86 }
三个线程函数

6.编写四个按钮的事件响应

 1 void CMultiDemoDlg::OnBnClickedBtnThreadRun()
 2 {
 3     //需要判断下线程是否创建成功
 4     if(g_hThreadOne!=0 && g_hThreadTwo!=0 && g_hThreadThree!=0)
 5     {
 6         //恢复所有线程运行
 7         ResumeThread(g_hThreadOne);
 8         ResumeThread(g_hThreadTwo);
 9         ResumeThread(g_hThreadThree);
10         //禁用掉当前按钮,防止重复启动
11         GetDlgItem(IDC_BTN_THREAD_RUN)->EnableWindow(FALSE);
12         //启用按钮_挂起或者恢复
13         GetDlgItem(IDC_BTN_THREAD_SUSPEND_RESUME)->EnableWindow(TRUE);
14     }
15 }
按钮_启动线程事件处理
 1 void CMultiDemoDlg::OnBnClickedBtnThreadSuspendResume()
 2 {
 3     //根据自身控件标题来判断是采用挂起还是恢复操作
 4     CString strCaption;
 5     GetDlgItem(IDC_BTN_THREAD_SUSPEND_RESUME)->GetWindowText(strCaption);
 6     if(strCaption == _T("挂起线程"))
 7     {
 8         //执行挂起操作前禁用掉按钮_通知事件退出
 9         GetDlgItem(IDC_BTN_THREAD_NOTIFYEXIT)->EnableWindow(FALSE);
10         //根据线程句柄依次挂起所有线程
11         if(g_hThreadOne!=0)
12         {
13             SuspendThread(g_hThreadOne);
14         }
15         if(g_hThreadTwo!=0)
16         {
17             SuspendThread(g_hThreadTwo);
18         }
19         if(g_hThreadThree!=0)
20         {
21             SuspendThread(g_hThreadThree);
22         }
23         //执行完挂起线程操作后,把控件标题改为恢复线程
24         GetDlgItem(IDC_BTN_THREAD_SUSPEND_RESUME)->SetWindowText(_T("恢复线程"));
25     }else if(strCaption == _T("恢复线程"))
26     {
27         //根据线程句柄依次恢复所有线程
28         if(g_hThreadOne!=0)
29         {
30             ResumeThread(g_hThreadOne);
31         }
32         if(g_hThreadTwo!=0)
33         {
34             ResumeThread(g_hThreadTwo);
35         }
36         if(g_hThreadThree!=0)
37         {
38             ResumeThread(g_hThreadThree);
39         }
40         
41         //执行完恢复线程操作后,把控件标题改为挂起线程
42         GetDlgItem(IDC_BTN_THREAD_SUSPEND_RESUME)->SetWindowText(_T("挂起线程"));
43         //启用按钮_通知事件退出
44         GetDlgItem(IDC_BTN_THREAD_NOTIFYEXIT)->EnableWindow(TRUE);
45     }
46 
47 }
按钮_挂起或者恢复事件处理
1 void CMultiDemoDlg::OnBnClickedBtnThreadNotifyexit()
2 {
3     //修改线程退出标志,通知线程进行退出
4     g_bThreadExit = true;
5 }
按钮_通知线程结束事件处理
 1 void CMultiDemoDlg::OnBnClickedBtnThreadTerminate()
 2 {
 3     //根据线程句柄依次强制终止所有线程(极其危险,除非万不得己才使用)
 4     if(g_hThreadOne!=0)
 5     {
 6         TerminateThread(g_hThreadOne,-1);
 7         CloseHandle(g_hThreadOne);
 8         g_hThreadOne = 0;
 9     }
10     if(g_hThreadTwo!=0)
11     {
12         TerminateThread(g_hThreadTwo,-1);
13         CloseHandle(g_hThreadTwo);
14         g_hThreadTwo = 0;
15     }
16     if(g_hThreadThree!=0)
17     {
18         TerminateThread(g_hThreadThree,-1);
19         CloseHandle(g_hThreadThree);
20         g_hThreadThree = 0;
21     }
22     //禁用按钮_挂起线程和按钮_通知线程退出
23     GetDlgItem(IDC_BTN_THREAD_SUSPEND_RESUME)->EnableWindow(FALSE);
24     GetDlgItem(IDC_BTN_THREAD_NOTIFYEXIT)->EnableWindow(FALSE);
25 }
按钮_强制终止线程事件处理

7.OnInitDialog中添加相应代码

 1 BOOL CMultiDemoDlg::OnInitDialog()
 2 {
 3     CDialogEx::OnInitDialog();
 4 
 5     // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
 6     //  执行此操作
 7     SetIcon(m_hIcon, TRUE);            // 设置大图标
 8     SetIcon(m_hIcon, FALSE);        // 设置小图标
 9 
10     //对三个椭圆绘制区域进行设置
11     RECT rectOne, rectTwo, rectThree;
12     GetDlgItem(IDC_STATIC_THREAD_1)->GetWindowRect(&rectOne);
13     g_rectEllipseOne.left = rectOne.left-40;
14     g_rectEllipseOne.top = rectOne.top-70;
15     g_rectEllipseOne.right = g_rectEllipseOne.left + 100;
16     g_rectEllipseOne.bottom = g_rectEllipseOne.top + 30;
17 
18     GetDlgItem(IDC_STATIC_THREAD_2)->GetWindowRect(&rectTwo);
19     g_rectEllipseTwo.left = rectTwo.left-40;
20     g_rectEllipseTwo.top = rectTwo.top-70;
21     g_rectEllipseTwo.right = g_rectEllipseTwo.left + 100;
22     g_rectEllipseTwo.bottom = g_rectEllipseTwo.top + 30;
23 
24     GetDlgItem(IDC_STATIC_THREAD_3)->GetWindowRect(&rectThree);
25     g_rectEllipseThree.left = rectThree.left-40;
26     g_rectEllipseThree.top = rectThree.top-70;
27     g_rectEllipseThree.right = g_rectEllipseThree.left + 100;
28     g_rectEllipseThree.bottom = g_rectEllipseThree.top + 30;
29 
30     //保存下当前窗口句柄,供线程内部使用
31     g_MainWnd = this->m_hWnd;
32 
33     //创建三个挂起线程
34     g_hThreadOne = CreateThread(NULL,0,ThreadOne,NULL,CREATE_SUSPENDED,NULL);
35     g_hThreadTwo = CreateThread(NULL,0,ThreadTwo,NULL,CREATE_SUSPENDED,NULL);
36     g_hThreadThree = CreateThread(NULL,0,ThreadThree,NULL,CREATE_SUSPENDED,NULL);
37 
38     return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
39 }
OnInitDialog

8.DestroyWindow添加相应代码

 1 BOOL CMultiDemoDlg::DestroyWindow()
 2 {
 3     //线程退出标志改为真
 4     g_bThreadExit = true;
 5     int nCount = 0;
 6     //根据线程句柄依次通知线程退出
 7     if(g_hThreadOne!=0)
 8     {
 9         //初始化时创建的是挂起线程,这里需要判断一下线程挂起计数,防止后面无限等待
10         //获取下上一次挂起计数
11       nCount = ResumeThread(g_hThreadOne);
12         //根据挂起计数进行恢复,挂起多少次就得恢复多少次
13         for (int i = 0; i<nCount; i++)
14         {
15             ResumeThread(g_hThreadOne);
16         }
17         //通过信号量一直等待线程正常退出
18         WaitForSingleObject(g_hThreadOne,INFINITE);
19         //关闭句柄释放内核资源
20         CloseHandle(g_hThreadOne);
21         g_hThreadOne = 0;
22     }
23     if(g_hThreadTwo!=0)
24     {
25         nCount = ResumeThread(g_hThreadTwo);
26         //根据挂起计数进行恢复,挂起多少次就得恢复多少次
27         for (int i = 0; i<nCount; i++)
28         {
29             ResumeThread(g_hThreadTwo);
30         }
31         //通过信号量一直等待线程正常退出
32         WaitForSingleObject(g_hThreadTwo,INFINITE);
33         //关闭句柄释放内核资源
34         CloseHandle(g_hThreadTwo);
35         g_hThreadTwo = 0;
36     }
37     if(g_hThreadThree!=0)
38     {
39         nCount = ResumeThread(g_hThreadThree);
40         for (int i = 0; i<nCount; i++)
41         {
42             ResumeThread(g_hThreadThree);
43         }
44         WaitForSingleObject(g_hThreadThree,INFINITE);
45         CloseHandle(g_hThreadThree);
46         g_hThreadThree = 0;
47     }
48 
49     return CDialogEx::DestroyWindow();
50 }
DestroyWindow

9. 资源视图中修改第2个按钮的Disable属性为真,初始化时禁用,当点击按钮_启动线程后再启用

posted @ 2019-12-18 17:40  SmallOverFllow  阅读(305)  评论(0编辑  收藏  举报