单线程实习进度条,以及多线程实现进度条(对文件的遍历)

最近在实现一个遍历文件夹,并把进度同步显示在进度条上的小程序。因为对MFC还不熟悉, 所以花了几天的时间研究实现遍历文件夹与进度条的同步实现。最简单的一种进度条的实现方法是单线程的,点一下按钮进度条动一下的方式,这种实现步骤如下:
(一)、单线程进度条实现
首先,添加Progress控件,然后给它添加变量m_pro。其次,添加两个按钮控件:开始、前进。然后,添加两个button事件。

void CDemo05Dlg::OnButton1() //开始按钮
   {
        // TODO: Add your control notification handler code here
        m_pro.SetRange(0,100);
        m_pro.SetStep(5);
        m_pro.SetPos(10);
   }
   void CDemo05Dlg::OnButton2() //前进按钮
   {
        // TODO: Add your control notification handler code here
        m_pro.StepIt();
   }

(二)、多线程进度条实现:遍历文件夹,同时进度条显示遍历进度

标题
遍历文件夹是一个可大可小的程序,当遍历的路径中文件个数很多时,遍历全部的文件需要耗很长时间,为了不让用户错误的认为系统进入死机的状态,我们给程序加入进度条。但是如果直接添加进度条,在程序中控制他的进度,结果进度条只向前进一步,仍然像死机状态。那该怎么办呢?原因是,我们处理比较大的计算程序时,程序和进度条显示实在同一个进程中进行的,这样程序会先处理计算程序,导致进度条无法实时更新。
解决办法就是把处理大程序的这段逻辑让一个单独的线程去处理,并利用postmessage或sendmessage给主界面发送消息,让主界面去更新进度条。
具体代码和步骤如下。

首先,文本框、按钮、静态文本框、进度条等控件,然后在cpp文件中定义一个关联事件:
#define WM_UPDATEDATA WM_USER + 1999 //定义事件

BEGIN_MESSAGE_MAP(CDemo05Dlg,CDialog)
    ON_MESSAGE(WM_UPDATEDATA,&CDemo05Dlg::OnUpdateData)//让事件与程序关联
END_MESSAGE_MAP

标题

第二步,在头文件中进行函数和变量的声明
afx_msg LRESULT OnUpdateData(WPARAM wParam,LPARAM lParam);
LRESULT onUpdateData(WPARAM wParam,LPARAM lParam);
static UINT MyThread(LPVOID pParam);//必须定义为static,线程函数可以且必须是全局函数或者是静态成员函数。
CWinThread *m_pMyThread;
int ProcessLogFile(CString filepath);
void setProcess(int value);
CString filePath;
int FileSearch(CString root);
int iProRange;//统计文件个数
CString m_FilePath;//全局变量,保存最初的文件路径
CProgressCtrl m_ProgCtrl;//进度条变量
CStatic m_Num;
int iCount;//统计机器码文件
CString szNum;
CString szFilepath;//FileSearch函数中使用

标题

第三步、实现关联程序(返回类型一定要是LRESULT)
//线程处理进度条
LRESULT CDemo05Dlg::OnUpdateData(WPARAM wParam,LPARAM lParam)
{
int iTemp = (int )wParam;
m_ProgCtrl.SetPos(iTemp);//设置进度条的值
UpdateData(FALSE);//实时更新主界面
return 0;
}

标题

第四步、创建一个独立的线程处理大程序
在OnInitial函数中,对m_pMyThread初始化:
m_pMyThread = NULL;
在【遍历文件夹】按钮下,创建线程:

void CDemo05Dlg::OnBnClickedBtn()
{
    // TODO: 在此添加控件通知处理程序代码
    iCount=0;//在遍历函数中统计文件数,chongzhi
    m_ProgCtrl.SetPos(0);//重置进度条
    GetDlgItem(IDC_EDIT1)->GetWindowTextW(m_FilePath);//获取指定的文件路径

    //MyThread为该大程序处理的入口函数,this为入口函数的参数 
    if (!m_FilePath.IsEmpty())
    {
        GetDlgItem(IDC_BTN)->EnableWindow(FALSE);//设置遍历按钮不可用
        //统计文件个数,设进度条的range
        FileSearch(m_FilePath);//统计文件个数,不是线程
        m_ProgCtrl.SetRange(0,iProRange);
        //创建新线程,实时更新进度条,将当前类this当做参数传递
        m_pMyThread = AfxBeginThread(CDemo05Dlg::MyThread, this);   

        GetDlgItem(IDC_BTN)->EnableWindow(TRUE);

    }
    else
    {
        AfxMessageBox(L"请选择路径!");
    }

}
统计文件个数函数FileSearch(m_FilePath),如下:

int CDemo05Dlg::FileSearch(CString root)
{
    CFileFind fileFinder;
    if (root.Right(1)!="/")
    {
        root+="/";
    }
    root+="*.*";
    BOOL res=fileFinder.FindFile(root);//遍历目录下的文件、文件夹
    while (res)
    {       
        res=fileFinder.FindNextFile();
        szFilepath=fileFinder.GetFilePath();
        if (fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件夹
        {
            FileSearch(szFilepath);// 递归
            //AfxMessageBox(L"遍历文件夹");
        }
        else if (!fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件
        {
            //获取文件类型
            CString fileName = fileFinder.GetFileName();
            int dotPos=fileName.ReverseFind('.');
            CString fileExt=fileName.Right(fileName.GetLength()-dotPos);
            if(fileExt == _T(".txt"))  //若是txt文件
            {
                CString szFileName = fileName.Left(fileName.GetLength()-4);  //获取文件名
                if (szFileName == L"MyTxt")
                {  
                    iProRange ++;//统计MyTxt文件个数
                }
            }
        }   
    }
    return iProRange;
}

标题

第五步、实现遍历文件夹逻辑

UINT CDemo05Dlg::MyThread(LPVOID pParam)//注意返回类型为UINT
    {
        CDemo05Dlg *p=(CDemo05Dlg*)pParam; //获取传参(this)的值,指当前的类,
        p->ProcessLogFile(p->m_FilePath);//调用生成函数,静态成员函数不可调用非静态变量,通过p间接调用。
        p->szNum.Format(L"%d",p->iCount);
        p->m_Num.SetWindowTextW(L"共 "+p->szNum+L"个文件");
        return 0;
    }

标题

第六步、向主线程发送消息让进度条实时更新

int CRealPipeSoftWareKeyVenderBatch::ProcessLogFile(CString root)  
     {
        CFileFind fileFinder;
        if (root.Right(1)!="/")
        {
            root+="/";
        }
        root+="*.*";
        BOOL res=fileFinder.FindFile(root);//遍历目录下的文件、文件夹
        while (res)
    {       
        setProcess(iCount);//调用进度条实时更新函数实时更新进度条
        res=fileFinder.FindNextFile();
        filePath=fileFinder.GetFilePath();
        if (fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件夹
        {           
            ProcessLogFile(filePath);// 递归
            //AfxMessageBox(L"遍历文件夹");
        }
        else if (!fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件
        {
            //获取文件类型
            CString fileName = fileFinder.GetFileName();
            int dotPos=fileName.ReverseFind('.');
            CString fileExt=fileName.Right(fileName.GetLength()-dotPos);
            if(fileExt == _T(".txt"))  //若是txt文件
            {
                CString szFileName = fileName.Left(fileName.GetLength()-4);  //获取文件名
                if (szFileName == L"MyTxt")
                {  
                    iCount ++;//统计MyTxt文件个数
                    szNum.Format(L"%d",iCount);
                    m_Num.SetWindowTextW(L"第"+szNum+L"个");
                    。
                    。
                    。
                    //此处可进行一些其他的,对找到的文件的操作
                    。
                    。
                    。
               }
            }
        }
    }   
}

最后,进度条更新函数

void CDemo05Dlg::setProcess(int value)
{
    //向主线程发送消息更新进度条
    SendMessage(WM_UPDATEDATA, value);
}

好了,运行程序看结果吧。

你可能感兴趣的:(多线程,同步,vc++,进度条)