Visual C++利用互斥量同步线程实现文件读取进度条
生活随笔
收集整理的這篇文章主要介紹了
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(),函數代碼如下:?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的Visual C++利用互斥量同步线程实现文件读取进度条的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MFC利用控制台输出调试信息的方法
- 下一篇: C++ Unicode和ANSII转换