Visual C++利用互斥量同步线程实现文件读取进度条

忘了原文的位置了。


一、前言

        文件读取进度条的实现可以有很多种方法,常用的是在读取文件的过程中隔一定时间向对话框发送消息以控制进度条的位置,但是这种方法很难确定隔多少时问发送一个消息,因为文件的大小是不确定的,时间间隔长了可能文件已经读取完了还没有发送消息,而消息发送得太频繁又会影响文件读取的效率。特别是在读取文本文件时你可能需要在每一个ReadString()函数之后都要发送一个消息,而在一些格式比较复杂的文件读写代码中(例如dxf文件的读取),这样的读取函数循环可能有几十处,在这样的代码中发送消息是很繁琐的事情。而利用线程同步则可以很好地解决这个问题。         进程是一个可执行的程序,由私有虚拟地址空间、代码、数据和其他操作系统资源(如进程创建的文件、管道、同步对象等)组成。一个应用程序可以有一个或多个进程,一个进程可以有一个或多个线程,其中一个是主线程。         线程是操作系统分时调度分配CPU时问的基本实体。一个线程可以执行程序的任意部分的代码,即使这部分代码被另一个线程并发地执行,一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。         创建一个新的进程必须加载代码,而线程要执行的代码已经被映射到进程的地址空间,所以创建、执行线程的速度比进程更快。另外,一个进程的所有线程共享进程的地址空间和全局变量简化了线程之间的通信,所以以线程为调度对象要比以进程为调度对象效率高。但是在几个线程并行运行时,可能会存在线程的同步问题。例如:两个线程同时对一个全局数组进行操作,线程A取得对该数组的控制权对数组进行写入,当写入还未完成时,控制权又由线程B取得,线程B 改变了该数组的数据,然后线程A又取得控制权进行读取,这样线程A获取的数据可能并不足其所需要的数据,这时就要用线程同步来解决这个问题。         Windows提供了几种同步对象来实现线程的同步,常用的有临界区(critical section)、互斥量(mutexe)、信号量(semaphore)、事件(event)和可等待的记时器(waitable timer)等。线程主要使用两个函数将它们设为睡眠来等待内核对象变为有信号:  


DWORD WaitForSingleObject(HANDLE hObject, DWORD dwTimeOut)
DWORD WaitForMultipleOblects(DWORD cObject, LPHANDLE lpHandles)
        函数WaitForSingleObject告诉系统线程在等待由参数hObject标识的内核对象变为有信号。参数dwTimeOut 告诉系统线程愿意等待多少毫秒。如果指定的内核对象在指定时间内没有变为有信号,系统就会唤醒线程,让它继续执行。函数的返回值有3 种:WAIT_OBJECT_0表示对象达到有信号状态;WAIT_TIMEOUT表示对象在dwTimeOut毫秒内未达到有信号状态;WAIT_ABANDONED表示对象是一个互斥量,由于被放弃而达到了有信号的状态。  


二、实现方法
        首先我们声明一个全局的文件指针g_pFile以存放要读取的文件指针,然后在主线程中指定要读取的文件,初始化g_pFile,并进行进度条对话框的创建。然后打开两个线程ReadDxfThreadProc(LPVOID lParam)和SetProgressPosThreadProc(LPVOID lParam),第一个线程用来读取文件,第二个线程则通过g_pFile指针来判断当前文件指针的位置,并向对话框发送消息以控制进度条的位置。这样我们将文件读取和向对话框发送消息分离,就不必在每个文件读取语句后都发送消息了,而且文件读取的效率也较高:         在编码过程中有以下几个问题要注意:         (l)在创建两个线程的时候必须将它们的优先级设置为同级,否则会造成一个线程已经结束了而另一个线程还没有开始。         (2)由于两个线程都要用到g_pFile指针,因此需要对两个线程进行同步,否则会出现异常。在本实例中我们利用互斥量来对两个线程进行同步。在声明全局文件指针的同时我们也声明一个互斥量句柄:HANDLE hMutex = NULL,并在主进程打开线程之前创建互斥量,对g_hMutex进行初始化:g_hMutex = CreateMutex(NULL, FALSE, NUlL);         (3)由于本实例中进度条对话框使用的是非模态划话框,因此除了需要定义设置进度条位置的消息响应函数之外,还需要定义销毁对话框的消息响应函数。         (4)在线程SetProgressPosThreadProc中我们通过全局变量g_pFile来探测文件读写的进度。当文件读写完毕以后我们需要对非模态的进度条对话框发送一个销毁对话框窗的消息以关闭对话框。         (5)当文件读取完毕以后,我们需要将全局的文件指针g_pFile销毁并置空。由于在 SetProgressPosThreadProc线程中我们循环的条件是文件当前位置小于文件的长度,所以当循环跳出时就说明读取文件的线程已经执行完毕,这时我们就可以关闭g_pFile指针了。如果将指针的关闭放在ReadDxfThreadProc线程中执行,则可能出现文件指针已关闭,而 SetProgressPosThreadProc线程中仍在调用文件指针的情况。  


三、程序实现

        (1)利用AppWizard创建一个MFC AppWizard(EXE)的单文档工程,取名为:ReadFile。

        (2)新建一个对话框,并为该对话框创建一个类CProgressDlg。

        (3)进度条控件和百分数的静态文本框定义变量:  

CProgressCtrl m_ctrlProgress;

CStrina m_szPercent;      

  (4)为对话框添加函数BOOL Create(),以创建非模态对话框。函数实现如下:  



BOOL CProgressDlg::Create()
{
    return CDialog::Create(CProgressDlg::IDD, NULL);
}        (5)在对话框初始化时要设置进度条的范围:  


BOOL CProgressDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_ctrlProgress.SetRange(l0, 100);
    return TRUE;
}        (6)由于线程是通过向对话框发送消息来控制进度条的位置和销毁对话框,所以我们必须在ProgressDlg.h文件中定义两个自定义消息:  


 #ifndef WM_UPDATE_DXF_DLG_POS                                       //更新进度条位置的消息
#define WM_UPDATE_DXF_DLG_POS WM_USER+10000; 
#endif
#ifndef WM_UPDATE_DXF_DLG_DESTROY                                    //销毁对话框的消息
#define WM_UPDATE_DXF_DLG_DESTROY WMUSER+10001
#endif        (7)建立消息响应函数。  


在ProgressDlg.cpp文件中END_MESSAGE_MAP()之前加入以下代码:
ON_MESSAGE(WM_UPDATE_DXF_DLG_POS, OnUpdateProgressPos)              //更新进度条位置的消息响应函数
ON_MESSAGE(WM_UPDATE_DXF_DLG_DESTROY, OnDestroyDlg)                  //销毁对话框的消息响应函数然后在ProgressDlg.h文件中DECLARE_MESSAGE_MAP()之前加入以下代码:  


afx_msg LRESULT OnUpdateProgressPos(WPARAM wp, LPARAM lp);
afx_msg LRESULT OnDestroyDlg(WPARAM wp, LPARAM lp);在ProgressDlg.cpp文件中添加消息响应函数实体:  


LRESULT CProgressDlg::OnUpdateProgressPos(WPARAM wp, LPARAM lp)     //传入的参数wp即是计算过后当前进度争的位置
{
    m_ctrIProgress.SetPos((int)wp);                                 //设置进度条位置
    m_szPercent.Forrnat("%d", (int)wp);
    m_szPercent += "%";
    UpdateData( FALSE);                                              //设置百分数显示的静态更本
    return 0;
}
LRESULT CProgressDlg::OnDestroyDlg(WPARAM wp, LPARAMp lp)
{
    DestroyWindow();                                                  //销毁对话框
    return 0;
}        (8)建立“确定”按钮响应函数。         由于我们采用的是非模态对话框,所以在此不能调用CDialog::OnOK()函数,也不能调用DestroyWindow()函数销毁对话框,因为此时可能SetProgressPosThreadProc进程还没有结束,还在向对话框发送消息,所以在此只是隐藏对话框,等到进程结束以后再发送消息销毁对话框。函数实体如下:  


void CProgressDlg::OnButtonClose()
{
    ShowWindow(SW_HIDE);
}        (9)在ReadFileView.cpp文件中定义全局变量和线程函数。代码如下:  


CStdioFile* g_pFile = NULL;                                //全局的正件指针
HANDLE g_hMutex = NULL;                                    //互斥量
DWORD WINAPI SelProqressPosTnreadProc(LPVOID lParam)       //发送进度各位置消息的进程
{
    CDialog* pDlg =(CDialog*)lParam;                      //传入的进度条对话框指针
    if(!pDlg) return 0;                                    //异常判断
    DWORD dPos = 0;                                        //当前文件指针的位置
    DWORD dLengtn = 1;                                     //文件的长度
    DWORD dProssPos = 0;                                   //计算出来的进度条的位置
    DWORD dPrePos = 0;                                     //记录前一个进度条的位置
    DWORD dw;
    dw = WaitForSingleObject(g_hMutex, INFINITE);          //等待互斥量有信号
    if(dw == WAIT_OBJECT_O);                               //判断互斥量是否为有信号
    {
        dLength = g_pFile->GetLength();                    //获取文件长度
    }
    else                                                   //废弃的信号,说明读取文件的线程有异常
    {
        ::SendMessage(pDlg->GetSafeHwnd(), WM_UPDATE_DXF_DLG_POS, 100, NULL);        //
        ::SendMessage(pDlg->GetSafeHwnd(), WM_UPDATE_DXF_DLG_DESTROY, NULL, NULL);   //向对话框发送消息,以销毁对话框
        return O;
    }
    ReleaseMutex(g_nMutex);                                //释放互斥量
    while(dPos < dLength)                                  //当文件未读完时执行循环
    {
        if(!pDlg) return 0;                                //异常判断
        dw = WaitForSingleObject(g_nMutex, INFINITE);      //等待互斥量有信号
        if{dw == WAIT_OBJECT_O)                           //等待到信号
        {
            dPos = g_pFile->GetPosition();                 //获取当前文件指针的位置
            dProssPos = DWORD(double(dPos)/dLength * 100); //计算进度条的位置
        }
        else                                               //废弃的信号
        {
            ::SendMessage(pDlg->GetSafeHwnd(), WM_UPDATE_DXF_DLG_POS, 100, NULL);       
            ::SendMessaqe(pDlg->GeiSafeHwnd(), WM_UPDATE_DXF_DLG_DESTROY, NULL, NULL);  //向对话框发送消息,以销毁对话框
            return 0;
        }
        ReleaseMutex(g_hMutex);                            //释放互斥量
        if(dProssPos != dPrePos)                           //进度条位置相同时不发送更新消息
        {
            ::SendMessage(pDlg->GetSafeHwnd(), WM_UPDATE_DXF_DLG_POS, dProssPos, NULL);
        }
        dPrePos = dProssPos;                               //当前位置变为前一位置
    }
    //由于计算位置时用的将double强制转换为DWORD型,有可能没有计算到100
    //所以需要发送一个消息将进度条位置刷新到100
    ::SendMessage(pDlg->GetSafeHwnd(), WM_UPDATE_DXF_DLG_POS, 100, NULL);
    Sleep(500);
    ::SendMessage(pDlg->GetSafeHwnd(), WM_UPDATE_DXF_DLG_DESTROY, NULL, NULL);          //向对话框发送消息,以销毁对话框
    //文件读取完毕以后要对文件指针进行关闭和销毁
    dw = WaitForSingleObject(g_hMutex, INFINITE);
    if(dw == WAIT_OBJECT_0)
    {
        g_pFile->Close();
        delete g_pFile;
        g_pFile = NULL;
    }
    else                                                    //废弃的信号
    {
        g_pFile->Close();
        delete g_pFile;
        g_pFile = NULL;
    }
    ReleaseMutex(g_hMutex);
    return 0;
}
    
DWORD WINAPI ReadDxfThreadProc(LPVOID lParam)              //读取文件的线程 
{
    CString strTernp = "";                                 //存放读取的字符
    BOOL bisEnd = TRUE;                                    //指示文件是否读取完毕
    DWORD dw,//互斥量信争结襄
    while(1)
    {
        if(!g_pFile)
            return 0;                                      //异常判断
        dw = WaitForSingleObject(g_hMutex,INFINITE);       
        if(dw == WAIT_OBJECT_0)                            //互斥量等待到信号
        {
            blsEnd = g_pFile->ReadString(strTemp);         //读取文件
        }
        else if(dw == WAIT_ABANDONED)
            return 0;
        ReleaseMUtex( g_hMutex):
        if(blsEnd == FALSE)
            break;
    }
    return 0;

}  

(10)在CReadFileView中添加进度条对话框对象成员。这个对象不能在调用时声明,因为主线程执行完后就会销毁对象,这样在线程中调用对话框指针就会出现异常。 

 在ReadFileView.h文件中添加:CProgressDlg m_ProgressDlg;  

 (11)在主框架菜单中添加下拉菜单“测试”,在“测试”菜单下添加子菜单“读取dxf文件”,然后为子菜单添加响应函数OnReadDxfFile(),函数代码如下: 




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

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

相关文章

组件模块化使用

// 使用$attr可以直接在父元素上拿数据让子元素使用或者是孙元素使用 <template><div>props:{{name}},{{age}} 或者 {{$props[name]}},{{$props[age]}} <br>attrs: {{$attrs[gender]}} 在$attrs里面只会有props没有注册的属性</div> </template>…

jboss与nginx_JBoss BRMS与JasperReports进行报告

jboss与nginx介绍 Jasperreports是可免费下载的库&#xff0c;可用于为Java EE应用程序生成丰富的报告。 本指南还提供了使用Jasper iReport设计器生成报告模板的步骤。 软件需求 JBoss BRMS 5.3&#xff08;从客户门户网站http://access.redhat.com &#xff09; JasperRep…

VS2010发布、打包安装程序超全超详细

找不到原文链接 1、 在vs2010 选择“新建项目”“其他项目类型”“ Visual StudioInstaller “安装项目”&#xff1a; 命名为&#xff1a;Setup1 。 这是在VS2010中将有三个文件夹&#xff0c; 1.“应用程序文件夹”表示要安装的应用程序需要添加的文件&#xff1b; 2.“…

IntelliJ IDEA2017 激活方法 最新的

今天打开电脑&#xff0c;非常不幸&#xff0c;idea出问题了&#xff01;&#xff01;&#xff01;大部分人以前应该都是用的以下方法&#xff1a;1. 到网站 http://idea.lanyus.com/ 获取注册码2.填入下面的license server:http://intellij.mandroid.cn/   http://idea.imsx…

介绍HawkFX

如前所述&#xff0c; 我开始玩JRubyFX 。 对我来说&#xff0c;学习一些新的最佳方法可以解决用例&#xff0c;因此我开始为Hawkular创建库存浏览器。 为什么选择JRubyFX&#xff1f; 让我们首先从“什么是JRubyFX”开始&#xff1f; JRubyFX是通过JRuby引入Ruby世界的JavaFX…

C++ Unicode和ANSII转换

构造字符串和转换字符串是不一样的&#xff0c;构造字符串时往往是添加标记&#xff0c;这个过程其实是告诉编译器应该怎么在内存中存储&#xff1b;一旦构造好&#xff0c;对于内存中的一块地址&#xff0c;这些标记符就没用了&#xff0c;这个时候就得使用转换函数转换了。对…

创建win32 dll

新建项目->win32项目&#xff08;不是win32控制台&#xff09;->下一步中选择dll&#xff08;如果想生成lib&#xff0c;勾选导出符号&#xff09;。此时编译会生成对应的dll文件&#xff0c;但没有lib&#xff0c;只有有导出符号的时候才会生成lib文件。导出符号可以使用…

蓝桥杯 有几个星期日的问题.

1949年的国庆节&#xff08;10月1日&#xff09;是星期六。 今年&#xff08;2012&#xff09;的国庆节是星期一。那么&#xff0c;从建国到现在&#xff0c;有几次国庆节正好是星期日呢&#xff1f;只要答案&#xff0c;不限手段&#xff01;可以用windows日历&#xff0c;win…

storm问题汇总

1.删除了本地topology导致无法启动nimbus 删除storm的自定义的库中的数据 删除zookeeper中配置的dataDir中的数据 重启服务即可 转载于:https://www.cnblogs.com/ives/p/9165850.html

带有API网关的AWS Lambda

在上一篇文章中&#xff0c;我向您展示了如何创建和部署AWS Lambda。 我们将继续这项工作&#xff0c;并只考虑更新该lambda的代码。 我们还将使用AWS API Gateway将REST端点添加到AWS Lambda。 因此&#xff0c;在继续之前……&#xff08;如果尚未使用&#xff09;&#xff…

获取焦点

win32 API:HWND SetFocus&#xff08;HWND hWnd&#xff09;MFC直接CWnd::SetFocus();参数&#xff1a;hWnd&#xff1a;接收键盘输入的窗口指针。若该参数为NULL&#xff0c;则击键被忽略。返回值&#xff1a;若函数调用成功&#xff0c;则返回原先拥有键盘焦点的窗口句柄。若…

树的问题?

题目&#xff1a; 一个深度为L的满k叉树有如下性质&#xff1a;第L层上的结点都是叶子结点&#xff0c;其余各层上每个结点都有k棵非空子树。如果按层数顺序从1开始对全部结点编号&#xff0c;问&#xff1a; &#xff08;1&#xff09;各层的结点数目是多少&#xff1f; &a…

HibernateTemplate

org.springframework.orm.hibernate5.HibernateTemplate 这是spring-orm-4.3.4.RELEASE.jar包中的一个类&#xff0c;这个类是对Hibernate进行了封装&#xff1b; 这是可以进行注入的属性&#xff0c;需要注入sessionFactory属性&#xff0c;因此我们需要创建一个sessionFactor…

使用电脑无线网卡分享网络命令

netsh wlan set hostednetwork modeallow ssidYourWifiName keyYourWifiPasswordnetsh wlan start hostednetworknetsh wlan show hostednetworkpause

STL_queue

STL__queue_的应用 调用的时候要有头文件&#xff1a; #include<stdlib.h> 或 #include<cstdlib> #include<queue> 详细用法: 定义一个queue的变量 queue<Type> que 查看是否为空范例 que.empt…

第四章: 4.1 logging模块 | 正则表达式

修改json数据然后写入json文件中 f open(1234.json,r,encodingutf-8) data f.read() data1 json.loads(data)data1[status] 1f1 open(1234.json,w,encodingutf-8)json.dump(data1,f1) hashlib md5值的用法 #加入下面这个就可以 password input(请输入密码:)m hashlib.md…

Java EE 6 VS Spring 3:Java EE杀死了Spring? 没门!

介绍 几天前&#xff0c;我在听Java Spotlight Podcast的第85集 。 在这次演讲中&#xff0c; Bert Ertman和Paul Bakker讨论了从Spring迁移到Java EE的问题。 基本上&#xff0c;在他们的介绍中&#xff0c;他们说&#xff0c;如今&#xff0c;选择Spring而不是Java EE是没有意…

蓝桥杯 n进制小数

n进制小数 将任意十进制正小数分别转换成2,3,4,5,6,7,8,9进制正小数&#xff0c;小数点后保留8位&#xff0c;并输出。例如&#xff1a;若十进制小数为0.795&#xff0c;则输出&#xff1a; 十进制正小数 0.795000 转换成 2 进制数为: 0.11001011 十进制正小数 0.795000 转换成…

状态栏编程(显示系统时间和进度条)

原文地址&#xff1a;http://welkangm.blog.163.com/blog/static/19065851020127941446182/ 显示系统时间 1、 在状态栏中设置两个新的栏位Timer和Progress。首先到ResourceView中编辑String Table&#xff0c;增加IDS_TIMER(时间),PROGRESS(进度)。然后在MainFrame中修改ind…

使用保险柜管理机密

您如何存储秘密&#xff1f; 密码&#xff0c;API密钥&#xff0c;安全令牌和机密数据属于秘密类别。 那是不应该存在的数据。 在容易猜测的位置&#xff0c;不得以纯文本格式提供。 实际上&#xff0c;不得在任何位置以明文形式存储它。 可以使用Spring Cloud Config Server或…