C++ 之win32多线程应用总结

InterlockedIncrement

函数的作用:

在多线程同时对一个变量访问时,保证一个线程访问变量时其他线程不能访问

  • 事件是很常用的多线程同步互斥机制
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, // SECURITY_ATTRIBUTES结构指针,可为NULLBOOL bManualReset,     // 手动/自动// TRUE:表示手动,在WaitForSingleObject后必须手动调用ResetEvent清除信号// FALSE:表示自动,在WaitForSingleObject后,系统自动清除事件信号BOOL bInitialState,        //初始状态,FALSE为无信号,TRUE为有信号LPCTSTR lpName         //事件的名称);

下边是使用演示:
在这里插入图片描述

在这里插入图片描述

CreateThread

  • 采用CreateThread()创建多线程程序,参考https://blog.csdn.net/cbnotes/article/details/8277180/在这里插入图片描述
  • 多线程实例

在此将写一个简单的多线程程序,用以展示多线程的功能和使用方法。该程序的主要的思想是画3个进度条,分别以多线程和单线程方式完成,大家可以比较一下。

说明:

(1)该程序还将和单线程做对比。

(2)由于给线程的函数传递了多个参数,所以采用结构体的方式传递参数。

(3)为了演示效果,采用了比较耗时的打点处理。

//线程函数声明
DWORD WINAPI ThreadProc(LPVOIDlpParam);
//为了传递多个参数,我采用结构体
struct threadInfo
{HWND hWnd;       //窗口句柄int  nOffset;    //偏移量COLORREF clrRGB; //颜色
};protected:
HANDLE hThead[3];    //用于存储线程句柄DWORD  dwThreadID[3];//用于存储线程的IDthreadInfo Info[3];   //传递给线程处理函数的参数
//----> 代码实现//单线程测试
void CMultiThread_1Dlg::OnBnClickedButton1()
{// TODO: 在此添加控件通知处理程序代码//使能按钮GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);CDC *dc = GetDC();CRect rt;GetClientRect(rt);dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//刷新背景dc->TextOut(97,470,"#1");dc->TextOut(297,470,"#2");dc->TextOut(497,470,"#3");//#1for (int i=0;i<460;i++){for (int j =10 ;j<200;j++){dc->SetPixel(j,460-i,RGB(255,0,0));}}//#2for (int i=0;i<460;i++){for (int j =210 ;j<400;j++){dc->SetPixel(j,460-i,RGB(0,255,0));}}//#3for (int i=0;i<460;i++){for (int j =410 ;j<600;j++){dc->SetPixel(j,460-i,RGB(0,0,255));}}ReleaseDC(dc);//使能按钮GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);
}//多线程测试
void CMultiThread_1Dlg::OnBnClickedButton2()
{// TODO: 在此添加控件通知处理程序代码CDC *dc = GetDC();CRect rt;GetClientRect(rt);dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//刷新背景dc->TextOut(97,470,"#1");dc->TextOut(297,470,"#2");dc->TextOut(497,470,"#3");//初始化线程的参数Info[0].hWnd = Info[1].hWnd = Info[2].hWnd = GetSafeHwnd();Info[0].nOffset = 10;Info[1].nOffset = 210;Info[2].nOffset = 410;Info[0].clrRGB = RGB(255,0,0);Info[1].clrRGB= RGB(0,255,0);Info[2].clrRGB = RGB(0,0,255);//创建线程for (int i = 0;i<3;i++){hThead[i] = CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);}ReleaseDC(dc);
}DWORD WINAPI ThreadProc(LPVOIDlpParam)
{threadInfo*Info = (threadInfo*)lpParam;CDC *dc = CWnd::FromHandle(Info->hWnd)->GetDC();for (int i=0;i<460;i++){for (int j=Info->nOffset;j<Info->nOffset+190;j++){dc->SetPixel(j,460-i,Info->clrRGB);}}DeleteObject(dc);return 0;
}

测试效果如下:
在这里插入图片描述
在这里插入图片描述

GetMessage

  • GetMessage是从调用线程的消息队列里取得一个消息并将其放于指定的结构。此函数可取得与指定窗口联系的消息和由PostThreadMessage寄送的线程消息。此函数接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。获取消息成功后,线程将从消息队列中删除该消息。函数会一直等待直到有消息到来才有返回值。
  • GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
参数:
lpMsg:指向MSG结构的指针,该结构从线程的消息队列里接收消息信息。
hWnd:取得其消息的窗口的句柄。当其值取NULL时,GetMessage为任何属于调用线程的窗口检索消息,线程消息通过PostThreadMessage寄送给调用线程。
wMsgFilterMin:指定被检索的最小消息值的整数。
wMsgFilterMax:指定被检索的最大消息值的整数。
返回值:如果函数取得WM_QUIT之外的其他消息,返回非零值。如果函数取得WM_QUIT消息,返回值是零。如果出现了错误,返回值是-1。例如,当hWnd是无效的窗口句柄或lpMsg是无效的指针时。若想获得更多   的错误信息,请调用GetLastError函数。

PeekMessage

  • PeekMessage 调用的一个例子:
PeekMessage(&msg,NULL,0,0,PM_REMOVE);

前面的4个参数(一个指向 MSG 结构的指针、一个窗口的句柄、两个值指示消息范围)与 GetMessage 的参数相同。
将第二、三、四个参数设置为 NULL 或 0时,表明我们想让 PeekMessage 返回程序中所有窗口的所有消息。

如果要将消息从消息队列中删除,则将 PeekMessage 的最后一个参数设置为 PM_REMOVE。
如果不希望删除消息,则将最后一个参数设置为 PM_NOREMOVE,这使得程序可以检查程序的消息队列中的下一个消息,而不实际删除它。

GetMessage 不将控制返回给程序,直到从程序的消息队列中获取消息,但是 PeekMessage 总是立刻返回,而不论一个消息是否出现。
当(应用程序的)消息队列中有一个消息时,PeekMessage 的返回值为 TRUE(非0),并且将按通常方式处理消息。当队列中没有消息时,PeekMessage 返回 FALSE(0)。
考虑如下例子:

// 普通的消息循环 
while( GetMessage(&msg,NULL,0,0) )
{TranslateMessage(&msg);DispatchMessage (&msg);
}
return msg.wParam;// 等价于
while( TRUE )
{if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) ){if(msg.message == WM_QUIT){break;}TranslateMessage(&msg);DispatchMessage(&msg);}else{// Other program lines to do some work}
}
return msg.wParam;

如果 PeekMessage 的返回值为 TRUE,则消息按通常方式进行处理。如果返回值为 FALSE,则在将控制返回给Windows(操作系统) 之前,还可以做一点工作(如显示另一个随机矩形)。

PeekMessage 不能从消息队列中删除 WM_PAINT 消息。
从队列中删除 WM_PAINT 消息的唯一方法是令窗口客户区的失效区域变得有效,这可以用 ValidateRect 和 ValidateRgn 或者 BeginPaint 和 EndPaint 对来完成。

不能使用如下所示的代码来清除消息队列中的所有消息:
while( PeekMessage(&msg,NULL,0,0,PM_REMOVE) );
这条语句从消息队列中删除 WM_PAINT 之外的所有消息。如果队列中有一个 WM_PAINT 消息,程序就会永远地陷在 while 循环中。

WaitForSingleObject

  • 等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);

当线程调用该函数时,第一个参数hObject标识一个能够支持被通知/未通知的内核对象。第二个参数dwMilliseconds.允许该线程指明,为了等待该对象变为已通知状态,它将等待多长时间。

  • 调用下面这个函数将告诉系统,调用函数准备等待到hProcess句柄标识的进程终止运行为止:
WaitForSingleObject(hProcess, INFINITE);

第二个参数告诉系统,调用线程愿意永远等待下去(无限时间量),直到该进程终止运行。
通常情况下, INFINITE是作为第二个参数传递给WaitForSingleObject的,不过也可以传递任何一个值(以毫秒计算)。顺便说一下, INFINITE已经定义为0xFFFFFFFF(或-1)。当然,传递INFINITE有些危险。如果对象永远不变为已通知状态,那么调用线程永远不会被唤醒,它将永远处于死锁状态,
不过,它不会浪费宝贵的CPU时间。

  • 下面是如何用一个超时值而不是INFINITE来调用WaitForSingleObject的例子:
DWORD dw = WaitForSingleObject(hProcess, 5000);
switch(dw)
{case WAIT_OBJECT_0:// The process terminated.break;case WAIT_TIMEOUT:// The process did not terminate within 5000 milliseconds.break;case WAIT_FAILED:// Bad call to function (invalid handle?)break;
}

上面这个代码告诉系统,在特定的进程终止运行之前,或者在5 0 0 0 m s时间结束之前,调用线程不应该变为可调度状态。因此,如果进程终止运行,那么这个
函数调用将在不到5000ms的时间内返回,如果进程尚未终止运行,那么它在大约5000ms时间内返回。注意,不能为dwMilliseconds传递0。如果传递了0,WaitForSingleObject函数将总是立即返回。WaitForSingleObject的返回值能够指明调用线程为什么再次变为可调度状态。如果线程等待的对象变为已通知状态,那么返回值是WAIT_OBJECT_0。如果设置的超时已经到期,则返回值是WAIT_TIMEOUT。如果将一个错误的值(如一个无效句柄)传递给WaitForSingleObject,那么返回值将是WAIT_FAILED(若要了解详细信息,可调用GetLastError)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/168455.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

支持Arm CCA的TF-A威胁模型

目录 一、简介 二、评估目标 2.1 假定 2.2 数据流图 三、威胁分析 3.1 威胁评估 3.1.1 针对所有固件镜像的一般威胁 3.1.2 引导固件可以缓解的威胁 3.1.3 运行时EL3固件可缓解的威胁 一、简介 本文针对支持Arm Realm Management Extension (RME)、实现Arm Confidentia…

Spark SQL 时间格式处理

初始化Spark Sql package pbcp_2023.clear_dataimport org.apache.spark.SparkConf import org.apache.spark.sql.SparkSession import org.apache.spark.sql.functions.{current_date, current_timestamp}object twe_2 {def main(args: Array[String]): Unit {val con new …

认识Linux操作系统

什么是操作系统&#xff1f; 操作系统是一款软硬件资源管理的软件Linux是一款具体的操作系统的品类&#xff08;Linux内核是用C语言写的&#xff09;centos7是一款具体的Linux操作系统 为什么要有操作系统&#xff1f; Linux操作系统 Linux是一种自由和开放源代码的类UNIX操…

Linux时间命令—— 显示时间,日历等

目录 1.date显示时间 1.1 常用的标记列表&#xff1a; 1.2 设定时间&#xff1a; 2.cal显示日历 3.时间戳 1.date显示时间 date 用法&#xff1a;date [OPTION] ... [FORMAT] 1.1 常用的标记列表&#xff1a; %H : 小时 (00..23) %M : 分钟 (00..59) %S : 秒 (00..61…

基于Python的面向对象分类实例Ⅱ

接上一部分继续介绍~ 一、地类矢量转栅格 这一步是为了能让地类值和影像的对象落在同一区域&#xff0c;从而将影像中的分割对象同化为实际地物类别。 train_fn r".\train_data1.shp" train_ds ogr.Open(train_fn) lyr train_ds.GetLayer() driver gdal.GetDrive…

python之pyqt专栏3-QT Designer

从前面两篇文章python之pyqt专栏1-环境搭建与python之pyqt专栏2-项目文件解析&#xff0c;我们对QT Designer有基础的认识。 QT Designer用来创建UI界面&#xff0c;保存的文件是"xxx.ui"文件&#xff0c;"xxx.ui"可以被pyuic转换为"xxx.py",而&…

Spring AOP:什么是AOP? 为什么要用AOP?如何学习AOP?

文章目录 &#x1f386;前言1.为什么要用 AOP3.如何学习去 AOP?3.1 AOP 的组成切面&#xff08;Aspect&#xff09;连接点&#xff08;Join Point&#xff09;切点&#xff08;Pointcut&#xff09;通知&#xff08;Advice&#xff09; 3. Spring AOP 实现3.1 普通的方式实现 …

Ubuntu20.04安装搜狗输入法

1、安装包下载 搜狗输入法linux-首页搜狗输入法for linux—支持全拼、简拼、模糊音、云输入、皮肤、中英混输https://shurufa.sogou.com/linux点击立即下载&#xff0c;根据自己的硬件选择deb安装包。 2、输入法安装 当第一步完成以后&#xff0c;页面会自动跳转至搜狗的安装…

【opencv】计算机视觉:实时目标追踪

目录 前言 解析 深入探究 前言 目标追踪技术对于民生、社会的发展以及国家军事能力的壮大都具有重要的意义。它不仅仅可以应用到体育赛事当中目标的捕捉&#xff0c;还可以应用到交通上&#xff0c;比如实时监测车辆是否超速等&#xff01;对于国家的军事也具有一定的意义&a…

25 Linux I2C 驱动

一、I2C简介 I2C老朋友了&#xff0c;在单片机里面也学过&#xff0c;现在再复习一下。I2C使用两条线在主控制器和从机之间进行数据通信。一条是 SCL(串行时钟线)&#xff0c;另外一条是 SDA(串行数据线)&#xff0c;这两条数据线需要接上拉电阻&#xff0c;总线空闲的时候 SCL…

docker部署phpIPAM

0说明 IPAM&#xff1a;IP地址管理系统 IP地址管理(IPAM)是指的一种方法IP扫描&#xff0c;IP地址跟踪和管理与网络相关的信息的互联网协议地址空间和IPAM系统。 IPAM软件和IP的工具,管理员可以确保分配IP地址仍然是当前和足够的库存先进的IP工具和IPAM服务。 IPAM简化并自动化…

管理类联考——数学——汇总篇——知识点突破——代数——函数——记忆

文章目录 整体文字提炼图像绘画 考点记忆/考点汇总——按大纲 本篇思路&#xff1a;根据各方的资料&#xff0c;比如名师的资料&#xff0c;按大纲或者其他方式&#xff0c;收集/汇总考点&#xff0c;即需记忆点&#xff0c;在通过整体的记忆法&#xff0c;比如整体信息很多&am…

电源控制系统架构(PCSA)之系统控制处理器组件

目录 6.4 系统控制处理器 6.4.1 SCP组件 SCP处理器Core SCP处理器Core选择 SCP处理器核内存 系统计数器和通用计时器 看门狗 电压调节器控制 时钟控制 系统控制 信息接口 电源策略单元 传感器控制 外设访问 系统访问 6.4 系统控制处理器 系统控制处理器(SCP)是…

基于Python 中创建 Sentinel-2 RGB 合成图像

一、前言 下面的python代码将带您了解如何从原始 Sentinel-2 图像创建 RGB 合成图像的过程。 免费注册后&#xff0c;可以从 Open Access Hub 下载原始图像。 请注意&#xff0c;激活您的帐户可能需要 24 小时&#xff01; 二、准备工作 &#xff08;1&#xff09;导入必要的库…

selenium的基础语法

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️山水速疾来去易&#xff0c;襄樊镇固永难开 ☁️定位页面的元素 参数:抽象类By里…

【从删库到跑路 | MySQL总结篇】数据库基础(增删改查的基本操作)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】&#x1f388; 本专栏旨在分享学习MySQL的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 重点放前面&am…

网络数据结构skb_buff原理

skb_buff基本原理 内核中sk_buff结构体在各层协议之间传输不是用拷贝sk_buff结构体&#xff0c;而是通过增加协议头和移动指针来操作的。如果是从L4传输到L2&#xff0c;则是通过往sk_buff结构体中增加该层协议头来操作&#xff1b;如果是从L4到L2&#xff0c;则是通过移动sk_…

【Linux】fork()

文章目录 一、fork()是什么&#xff1f;二、fork()干了什么&#xff1f;三、fork()怎么用&#xff1f; 一、fork()是什么&#xff1f; fork()函数其实是在Linux系统中用于创建一个新的进程。让我们看看Linux中是怎么描述的&#xff1f;运行man fork。 RETURN VALUE On success…

英特尔和 ARM 将合作开发移动芯片技术,如何看待双方合作?

英特尔和 ARM 将合作开发移动芯片技术&#xff0c;如何看待双方合作&#xff1f; 最近市场传出Arm要自产芯片&#xff0c;供智能手机与笔电等使用后&#xff0c;外媒指Arm自产芯片将由英特尔晶圆代工部门打造&#xff0c;变成英特尔晶圆代工客户。将采用英特尔18A工艺&#xff…

利用Nginx与php处理方式不同绕过Nginx_host实现SQL注入

目录 首先需要搭建环境 nginxphpmysql环境&#xff1a; 搭建网站 FILTER_VALIDATE_EMAIL 绕过 方法1&#xff1a;冒号号分割host字段 方法2&#xff1a;冒号号分割host字段 方法3&#xff1a;SNI扩展绕过 首先需要搭建环境 nginxphpmysql环境&#xff1a; php安装包&a…