忘了原文的位置了。

一、前言

文件读取进度条的实现可以有很多种方法,常用的是在读取文件的过程中隔一定时间向对话框发送消息以控制进度条的位置,但是这种方法很难确定隔多少时问发送一个消息,因为文件的大小是不确定的,时间间隔长了可能文件已经读取完了还没有发送消息,而消息发送得太频繁又会影响文件读取的效率。特别是在读取文本文件时你可能需要在每一个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(),函数代码如下:

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

  1. Linux多线程——使用互斥量同步线程

    前文再续,书接上一回,在上一篇文章:Linux多线程--使用信号量同步线程中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两个 ...

  2. 多线程编程(3): 使用互斥锁同步线程

    python多线程编程(3): 使用互斥锁同步线程 问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据n ...

  3. Qt实战案例(41)——利用QWinTaskbarButton和QWinTaskbarProgress类实现任务栏进度条的显示

    目录 一.项目介绍 二.项目基本配置 三.UI界面设置 四.主程序实现 4.1 pro文件 4.2 widget.h头文件 4.3 widget.cpp源文件 五.效果演示 一.项目介绍 任务栏进度条 ...

  4. python多线程编程(2): 使用互斥锁同步线程

    上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的代码: # ...

  5. python 线程锁_python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  6. python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  7. python多线程同步与互斥_python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  8. *(已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法

    Visual Studio 2019中的安装问题详细解决方法 前言 一.下载地址 二.问题解决 1.连接WIFI 2.修改DNS 刷新本地DNS解析缓存(分割线,7-13更新) 前言 因为最近要学习C ...

  9. 利用jQuery实现ajax下载文件时进度条

    实现原理:模拟一个虚拟的进度,利用里面子元素的宽度变化来实现进度增长, 关键属性:transition 预览: 进度条 html <div class="lineProgress&qu ...

最新文章

  1. 独家 | 5大冠军出炉!李开复的AI挑战赛,冠军们聊到了这么些感受
  2. 剑指offer--变态跳台阶--递归和循环
  3. 《sql语句练习1》
  4. 类和对象编程(六):内联函数
  5. [刘阳Java]_Spring相关配置介绍_第5讲
  6. MacBook Pro 16英寸样机模型正面视图psd素材
  7. Java基础--序列化和反序列化
  8. centos7 设备 mariadb-10
  9. 过程装备安装调试技术实训装置QY-GCKZ04
  10. LCD显示屏的器件选择和驱动电路设计
  11. iView组件+Django实现前后端分离上传图片
  12. Pilosa文档翻译(二)入门指南
  13. python拟合二次函数_Python 最小二乘法 拟合 二次曲线
  14. 基于JAVA美发店预约系统设计与实现
  15. Sublime Text 怎么使用打开md,替代Typora
  16. 比赛比分html模板,HTML5 球赛比分牌
  17. (2022保姆级教程)使用Java向邮箱发送邮件、验证码
  18. 信息系统可行性分析报告的一般格式
  19. 2019寒假安排计划
  20. 苹果电脑以及iPhone上截屏的方法介绍

热门文章

  1. Excel完成将多行多列数据转化为一列
  2. 【渝粤教育】国家开放大学2018年秋季 1320T关系营销 参考试题
  3. [渝粤教育] 西南科技大学 英语语法1 在线考试复习资料
  4. 新一代蓝牙对工业物联网(IIOT)的应用
  5. oracle tsn文件,无法启动OracleOraDB10g_home1TSNListener服务
  6. geteditor p 取消自动_2020百度网盘超级会员怎么取消自动续费?
  7. 【MFC系列-第33天】链接控件自绘技术
  8. Thread类的有关常用方法
  9. Java数组(1)--数组概述
  10. linux如何判断同名进程个数,Linux下判断是否存在多个同名进程