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


    编写一个多线程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
    ");
    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
    ");
    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
    ");
    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属性为真,初始化时禁用,当点击按钮_启动线程后再启用

  • 相关阅读:
    在 docker 容器中捕获信号
    python入门二维码生成
    SSH 端口转发
    Python之模块与包
    滑块验证demo示例
    上下界网络流初探
    大整数模板
    计算几何模板
    关于差分约束系统的脑洞
    并查集,以及可拆分并查集
  • 原文地址:https://www.cnblogs.com/fzxiaoyi/p/12061130.html
Copyright © 2020-2023  润新知