VC多線程編程詳解。本站提示廣大學習愛好者:(VC多線程編程詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是VC多線程編程詳解正文
本文實例講述了VC多線程編程概念與技能,分享給年夜家供年夜家參考。詳細剖析以下:
1、多線程編程要點
線程是過程的一條履行途徑,它包括自力的客棧和CPU存放器狀況,每一個線程同享一切的過程資本,包含翻開的文件、旌旗燈號標識及靜態分派的內存等。一個過程內的一切線程應用統一個地址空間,而這些線程的履行由體系調劑法式掌握,調劑法式決議哪一個線程可履行和甚麼時刻履行線程。線程有優先級別,優先權較低的線程必需比及優先權較高的線程履行完後再履行。在多處置器的機械上,調劑法式可將多個線程放到分歧的處置器上去運轉,如許可以使處置器義務均衡,並進步體系的運轉效力。
Windows是一種多義務的操作體系,在Windows的一個過程內包括一個或多個線程。32位Windows情況下的Win32 API供給了多線程運用法式開辟所須要的接口函數,而應用VC中供給的尺度C庫也能夠開辟多線程運用法式,響應的MFC類庫封裝了多線程編程的類,用戶在開辟時可依據運用法式的須要和特色選擇響應的對象。為了使年夜家能周全地懂得Windows多線程編程技巧,本文將重點引見Win32 API和MFC兩種方法下若何編制多線程法式。
多線程編程在Win32方法下和MFC類庫支撐下的道理是分歧的,過程的主線程在任何須要的時刻都可以創立新的線程。當線程履行完後,主動終止線程; 當過程停止後,一切的線程都終止。一切運動的線程同享過程的資本,是以,在編程時須要斟酌在多個線程拜訪統一資本時發生抵觸的成績。當一個線程正在拜訪某過程對象,而另外一個線程要轉變該對象,便可能會發生毛病的成果,編程時要處理這個抵觸。
2、Win32 API下的多線程編程
Win32 API是Windows操作體系內核與運用法式之間的界面,它將內核供給的功效停止函數包裝,運用法式經由過程挪用相干函數而取得響應的體系功效。為了向運用法式供給多線程功效,Win32 API函數集中供給了一些處置多線程法式的函數集。直接用Win32 API停止法式設計具有許多長處: 基於Win32的運用法式履行代碼小,運轉效力高,然則它請求法式員編寫的代碼較多,且須要治理一切體系供給給法式的資本。用Win32 API直接編寫法式請求法式員對Windows體系內核有必定的懂得,會占用法式員許多時光對體系資本停止治理,因此法式員的任務效力下降。
1. 用Win32函數創立和終止線程
Win32函數庫中供給了操作多線程的函數,包含創立線程、終止線程、樹立互斥區等。在運用法式的主線程或許其他運動線程中創立新的線程的函數以下:
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
假如創立勝利則前往線程的句柄,不然前往NULL。創立了新的線程後,該線程就開端啟動履行了。但假如在dwCreationFlags中應用了CREATE_SUSPENDED特征,那末線程其實不立時履行,而是先掛起,比及挪用ResumeThread後才開端啟動線程,在這個進程中可以挪用上面這個函數來設置線程的優先權:
BOOL SetThreadPriority(HANDLE hThread,int nPriority);
當挪用線程的函數前往後,線程主動終止。假如須要在線程的履行進程中終止則可挪用函數:
VOID ExitThread(DWORD dwExitCode);
假如在線程的裡面終止線程,則可挪用上面的函數:
BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);
但應留意: 該函數能夠會惹起體系不穩固,並且線程所占用的資本也不釋放。是以,普通情形下,建議不要應用該函數。
假如要終止的線程是過程內的最初一個線程,則線程被終止後響應的過程也應終止。
2. 線程的同步
在線程體內,假如該線程完整自力,與其他線程沒稀有據存取等資本操作上的抵觸,則可依照平日單線程的辦法停止編程。然則,在多線程處置時情形經常不是如許,線程之間常常要同時拜訪一些資本。因為對同享資本停止拜訪惹起抵觸是弗成防止的,為懂得決這類線程同步成績,Win32 API供給了多種同步掌握對象來贊助法式員處理同享資本拜訪抵觸。在引見這些同步對象之前先引見一劣等待函數,由於一切掌握對象的拜訪掌握都要用到這個函數。
Win32 API供給了一組能使線程壅塞其本身履行的期待函數。這些函數在其參數中的一個或多個同步對象發生了旌旗燈號,或許跨越劃定的期待時光才會前往。在期待函數未前往時,線程處於期待狀況,此時線程只消費很少的CPU時光。應用期待函數既可以包管線程的同步,又可以進步法式的運轉效力。最經常使用的期待函數是:
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
而函數WaitForMultipleObject可以用來同時監測多個同步對象,該函數的聲明為:
DWORD WaitForMultipleObject(DWORD nCount,CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD dwMilliseconds);
(1)互斥體對象
Mutex對象的狀況在它不被任何線程具有時才有旌旗燈號,而當它被具有時則無旌旗燈號。Mutex對象很合適用來調和多個線程對同享資本的互斥拜訪。可按以下步調應用該對象:
起首,樹立互斥體對象,獲得句柄:
HANDLE CreateMutex();
然後,在線程能夠發生抵觸的區域前(即拜訪同享資本之前)挪用WaitForSingleObject,將句柄傳給函數,要求占用互斥對象:
dwWaitResult = WaitForSingleObject(hMutex,5000L);
同享資本拜訪停止,釋放對互斥體對象的占用:
ReleaseMutex(hMutex);
互斥體對象在統一時辰只能被一個線程占用,當互斥體對象被一個線程占用時,如有另外一線程想占用它,則必需比及前一線程釋放後能力勝利。
(2)旌旗燈號對象
旌旗燈號對象許可同時對多個線程同享資本停止拜訪,在創立對象時指定最年夜可同時拜訪的線程數。當一個線程請求拜訪勝利後,旌旗燈號對象中的計數器減一,挪用ReleaseSemaphore函數後,旌旗燈號對象中的計數器加一。個中,計數器值年夜於或等於0,但小於或等於創立時指定的最年夜值。假如一個運用在創立一個旌旗燈號對象時,將其計數器的初始值設為0,就壅塞了其他線程,掩護了資本。等初始化完成後,挪用ReleaseSemaphore函數將其計數器增長至最年夜值,則可停止正常的存取拜訪。可按以下步調應用該對象:
起首,創立旌旗燈號對象:
HANDLE CreateSemaphore();
或許翻開一個旌旗燈號對象:
HANDLE OpenSemaphore();
然後,在線程拜訪同享資本之前挪用WaitForSingleObject。
同享資本拜訪完成後,應釋放對旌旗燈號對象的占用:
ReleaseSemaphore();
(3)事宜對象
事宜對象(Event)是最簡略的同步對象,它包含有旌旗燈號和無旌旗燈號兩種狀況。在線程拜訪某一資本之前,須要期待某一事宜的產生,這時候用事宜對象最適合。例如:只要在通訊端口緩沖區收到數據後,監督線程才被激活。
事宜對象是用CreateEvent函數樹立的。該函數可以指定事宜對象的類和事宜的初始狀況。假如是手工重置事宜,那末它老是堅持有旌旗燈號狀況,直到用ResetEvent函數重置成無旌旗燈號的事宜。假如是主動重置事宜,那末它的狀況在單個期待線程釋放後會主動變成無旌旗燈號的。用SetEvent可以把事宜對象設置成有旌旗燈號狀況。在樹立事宜時,可認為對象定名,如許其他過程中的線程可以用OpenEvent函數翻開指命名字的事宜對象句柄。
(4)排擠區對象
在排擠區中異步履行時,它只能在統一過程的線程之間同享資本處置。固然此時下面引見的幾種辦法都可應用,然則,應用排擠區的辦法則使同步治理的效力更高。
應用時先界說一個CRITICAL_SECTION構造的排擠區對象,在過程應用之前挪用以下函數對對象停止初始化:
VOID InitializeCriticalSection(LPCRITICAL_SECTION);
當一個線程應用排擠區時,挪用函數:EnterCriticalSection或許TryEnterCriticalSection;
當請求占用、加入排擠區時,挪用函數LeaveCriticalSection,釋放對排擠區對象的占用,供其他線程應用。
3、基於MFC的多線程編程
MFC是微軟的VC開辟集成情況中供給給法式員的基本函數庫,它用類庫的方法將Win32 API停止封裝,以類的方法供給給開辟者。因為其疾速、簡捷、功效壯大等特色深受寬大開辟者愛好。是以,建議應用MFC類庫停止運用法式的開辟。
在VC++附帶的MFC類庫中,供給了對多線程編程的支撐,根本道理與基於Win32 API的設計分歧,但因為MFC對同步對象做了封裝,是以完成起來加倍便利,防止了對象句柄治理上的煩瑣任務。
在MFC中,線程分為兩種:任務線程和用戶接口線程。任務線程與後面所述的線程分歧,用戶接口線程是一種可以或許吸收用戶的輸出、處置事宜和新聞的線程。
1. 任務線程
任務線程編程較為簡略,設計思緒與後面所講的根本分歧: 一個根本函數代表了一個線程,創立並啟動線程後,線程進入運轉狀況; 假如線程用到同享資本,則須要停止資本同步處置。這類方法創立線程並啟動線程時可挪用函數:
CWinThread*AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority= THREAD_PRIORITY_NORMAL,
UINT nStackSize =0,
DWORD dwCreateFlags=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
參數pfnThreadProc是線程履行體函數,函數本相為: UINT ThreadFunction( LPVOID pParam)。
參數pParam是傳遞給履行函數的參數;
參數nPriority是線程履行權限,可選值:
THREAD_PRIORITY_NORMAL、THREAD_PRIORITY_LOWEST、THREAD_PRIORITY_HIGHEST、THREAD_PRIORITY_IDLE。
參數dwCreateFlags是線程創立時的標記,可取值CREATE_SUSPENDED,表現線程創立後處於掛起狀況,挪用ResumeThread函數後線程持續運轉,或許取值“0”表現線程創立後處於運轉狀況。
前往值是CWinThread類對象指針,它的成員變量m_hThread為線程句柄,在Win32 API方法下對線程操作的函數參數都請求供給線程的句柄,所以當線程創立後可使用一切Win32 API函數對pWinThread->m_Thread線程停止相干操作。
留意:假如在一個類對象中創立和啟動線程時,應將線程函數界說成類外的全局函數。
2. 用戶接口線程
基於MFC的運用法式有一個運用對象,它是CWinApp派生類的對象,該對象代表了運用過程的主線程。當線程履行完並加入線程時,因為過程中沒有其他線程存在,過程主動停止。類CWinApp從CWinThread派生出來,CWinThread是用戶接口線程的根本類。我們在編寫用戶接口線程時,須要從CWinThread派生我們本身的線程類,ClassWizard可以贊助我們完成這個任務。
先用ClassWizard派生一個新的類,設置基類為CwinThread。留意:類的DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏是必須的,由於創立線程時須要靜態創立類的對象。依據須要可將初始化和停止代碼分離放在類的InitInstance和ExitInstance函數中。假如須要創立窗口,則可在InitInstance函數中完成。然後創立線程並啟動線程。可以用兩種辦法來創立用戶接口線程,MFC供給了兩個版本的AfxBeginThread函數,個中一個用於創立用戶接口線程。第二種辦法分為兩步停止:起首,挪用線程類的結構函數創立一個線程對象;其次,挪用CWinThread::CreateThread函數來創立該線程。線程樹立並啟動後,在線程函數履行進程中一向有用。假如是線程對象,則在對象刪除之前,先停止線程。CWinThread曾經為我們完成了線程停止的任務。
3. 線程同步
後面我們引見了Win32 API供給的幾種有關線程同步的對象,在MFC類庫中對這幾個對象停止了類封裝,它們有一個配合的基類CSyncObject,它們的對應關系為: Semaphore對應CSemaphore、Mutex對應CMutex、Event對應CEvent、CriticalSection對應CCriticalSection。別的,MFC對兩個期待函數也停止了封裝,即CSingleLock和CMultiLock。因四個對象用法類似,在這裡就以CMutex為例停止解釋:
創立一個CMutex對象:
CMutex mutex(FALSE,NULL,NULL);
或 CMutex mutex;
當各線程要拜訪同享資本時應用上面代碼:
CSingleLock sl(&mutex);
sl.Lock();
if(sl.IsLocked())
//對同享資本停止操作...
sl.Unlock();
4、停止語
假如用戶的運用法式須要多個義務同時停止響應的處置,則應用多線程是較幻想的選擇。這裡,提示年夜家留意的是在多線程編程時要特殊當心處置資本同享成績和多線程調試成績。
願望本文所述對年夜家的VC法式設計有所贊助。