多线程(二)基本使用方法
编写一个多线程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 }
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 }