程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> VC++中過程與多過程治理的辦法詳解

VC++中過程與多過程治理的辦法詳解

編輯:關於C++

VC++中過程與多過程治理的辦法詳解。本站提示廣大學習愛好者:(VC++中過程與多過程治理的辦法詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是VC++中過程與多過程治理的辦法詳解正文


本文實例講述了VC++中過程與多過程治理的辦法,分享給年夜家供年夜家參考。詳細辦法剖析以下:

摘要: 本文重要引見了多義務治理中的多過程治理技巧,對過程的互斥運轉、子過程的創立與停止等作了較具體的論述。

症結詞: VC++6.0;過程;情況變量;子過程

過程

  過程是以後操作體系下一個被加載到內存的、正在運轉的運用法式的實例。每個過程都是由內查對象和地址空間所構成的,內查對象可讓體系在其內寄存有關過程的統計信息並使體系可以或許以此來治理過程,而地址空間則包含了一切法式模塊的代碼和數據和線程客棧、堆分派空間等靜態分派的空間。過程僅僅是一個存在,是不克不及單獨完成任何操作的,必需具有至多一個在其情況下運轉的線程,並由其擔任履行在過程地址空間內的代碼。在過程啟動的同時即同時啟動了一個線程,該線程被稱作主線程或是履行線程,由此線程可以持續創立子線程。假如主線程加入,那末過程也就沒有存在的能夠了,體系將主動取消該過程並完成對其地址空間的釋放。
  加載到過程地址空間的每個可履行文件或靜態鏈接庫文件的映象都邑被分派一個與之相干聯的全局獨一的實例句柄(Hinstance)。該實例句柄現實是一個記載有過程加載地位的根本內存地址。過程的實例句柄在法式進口函數WinMain()中經由過程第一個參數HINSTANCE hinstExe傳遞,其現實值即為過程所應用的根本地址空間的地址。關於VC++鏈接法式所鏈接發生的法式,其默許的根本地址空間地址為0x00400000,如沒有需要普通不要修正該值。在法式中,可以經由過程GetModuleHandle()函數獲得指定模塊所應用的根本地址空間。

子過程的創立

  過程的創立經由過程CreateProcess()函數來完成,CreateProcess()經由過程創立一個新的過程及在其地址空間內運轉的主線程來啟動並運轉一個新的法式。詳細的,在履行CreateProcess()函數時,起首由操作體系擔任創立一個過程內查對象,初始化計數為1,並立刻為新過程創立一塊虛擬地址空間。隨後將可履行文件或其他任何須要的靜態鏈接庫文件的代碼和數據裝載到該地址空間中。在創立主線程時,也是起首由體系擔任創立一個線程內查對象,並初始化為1。最初啟動主線程並履行過程的進口函數WinMain(),完成對過程和履行線程的創立。
  CreateProcess()函數的原型聲明以下:
BOOL CreateProcess(
 LPCTSTR lpApplicationName, // 可履行模塊名
 LPTSTR lpCommandLine, // 敕令行字符串
 LPSECURITY_ATTRIBUTES lpProcessAttributes, // 過程的平安屬性
 LPSECURITY_ATTRIBUTES lpThreadAttributes, // 線程的平安屬性
 BOOL bInheritHandles, // 句柄繼續標記
 DWORD dwCreationFlags, // 創立標記
 LPVOID lpEnvironment, // 指向新的情況塊的指針
 LPCTSTR lpCurrentDirectory, // 指向以後目次名的指針
 LPSTARTUPINFO lpStartupInfo, // 指向啟動信息構造的指針
 LPPROCESS_INFORMATION lpProcessInformation // 指向過程信息構造的指針
);
  在法式設計時,某一個詳細的功效模塊可以經由過程函數或是線程等分歧的情勢來完成。關於統一過程而言,這些函數、線程都是存在於統一個地址空間下的,並且在履行時,年夜多只對與其相干的一些數據停止處置。假如算法存在某種毛病,將有能夠損壞與其同處一個地址空間的其他一些主要內容,這將形成比擬嚴重的效果。為掩護地址空間中的內容可以斟酌將那些須要對地址空間中的數據停止拜訪的操作部門放到別的一個過程的地址空間中運轉,而且只許可其拜訪原過程地址空間中的相干數據。詳細的,可在過程中經由過程CreateProcess()函數去創立一個子過程,子過程在全體處置進程中只對父過程地址空間中的相干數據停止拜訪,從而可以掩護父過程地址空間中與以後子過程履行義務有關的全體數據。關於這類情形,子過程所表現出來的感化同函數和線程比擬類似,可以算作是父過程在運轉時代的一個進程。為此,須要由父過程來控制子過程的啟動、履行和加入。上面這段代碼即展現了此進程:
// 暫時變量
CString sCommandLine;
char cWindowsDirectory[MAX_PATH];
char cCommandLine[MAX_PATH];
DWORD dwExitCode;
PROCESS_INFORMATION pi;
STARTUPINFO si = {sizeof(si)};
// 獲得Windows目次
GetWindowsDirectory(cWindowsDirectory, MAX_PATH);
// 啟動"記事本"法式的敕令行
sCommandLine = CString(cWindowsDirectory) + "//NotePad.exe";
::strcpy(cCommandLine, sCommandLine);
// 啟動"記事本"作為子過程
BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ret) {
 // 封閉子過程的主線程句柄
 CloseHandle(pi.hThread);
 // 期待子過程的加入
 WaitForSingleObject(pi.hProcess, INFINITE);
 // 獲得子過程的加入碼
 GetExitCodeProcess(pi.hProcess, &dwExitCode);
 // 封閉子過程句柄
 CloseHandle(pi.hProcess);
}
  此段代碼起首經由過程CreateProcess()創立Windows自帶的“記事本”法式為子過程,子過程啟動後父過程經由過程WaitForSingleObject()函數期待其履行的停止,在子過程沒有加入前父過程是一向處於壅塞狀況的,這裡子過程的感化同單線程中的函數相似。一旦子過程加入,WaitForSingleObject()函數所期待的pi.hProcess對象將獲得告訴,父過程將得以持續,若有需要可以經由過程GetExitCodeProcess()來獲得子過程的加入代碼。
  比擬而言,更多的情形是父過程在啟動完子過程後就再不與其停止任何數據交流和通信,由其創立的子過程的履行勝利與否均與父過程有關。很多年夜型軟件在設計時也多采取了這類思惟,將某些功效完整經由過程自力的運用法式來完成,當須要履行某操作時只需經由過程主法式啟動響應的子過程便可,詳細的處置任務均由子過程去完成。這類子過程的創立進程更加簡略,例如關於下面那段代碼只需去除對子過程句柄pi.hProcess的期待便可:
BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ret) {
 // 封閉子過程的主線程句柄
 CloseHandle(pi.hThread);
 // 封閉子過程句柄
 CloseHandle(pi.hProcess);
}
  可以經由過程dwCreationFlags參數在創立過程時設置子過程的優先級。後面的示例代碼在創立子過程時應用的均是默許的優先級,假如要將優先級設置為高,可以修正以下:
BOOL ret = CreateProcess(NULL, cCommandLine, NULL, NULL, FALSE, HIGH_PRIORITY_CLASS, NULL, NULL, &si, &pi);
  假如在過程創立時沒有特殊設置優先級,可以經由過程SetPriorityClass()函數來靜態設定,該函數須要待操作過程的句柄和優先級標識符作為進口參數,函數原型為:
BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);
  關於後面沒有設定優先級的例子代碼,可以在子過程啟動後由父過程來靜態轉變其優先級設置:
SetPriorityClass(pi.hProcess, HIGH_PRIORITY_CLASS);
  或是由子過程在其啟動後自行轉變優先級設置,須要留意的是這時候過程句柄應設置為子過程本身的句柄,可經由過程GetCurrentProcess()函數來獲得:
HANDLE hProcess = GetCurrentProcess();
SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS);

過程的互斥運轉

  正常情形下,一個過程的運轉普通是不會影響到其他正在運轉的過程的。然則關於某些有特別請求的如以獨有方法應用串行口等硬件裝備的法式就請求在其過程運轉時代不許可其他試圖應用此端口裝備的法式運轉的,並且此類法式平日也不許可運轉統一個法式的多個實例。這就引出了過程互斥的成績。
  完成過程互斥的焦點思惟比擬簡略:過程在啟動時起首檢討以後體系能否曾經存在有此過程的實例,假如沒有,過程將勝利創立並設置標識實例曾經存在的標志。爾後再創立過程時將會經由過程該標志而知曉其實例曾經存在,從而包管過程在體系中只能存在一個實例。詳細可以采用內存映照文件、著名事宜量、著名互斥量和全局同享變量等多種辦法來完成。上面就分離對個中具有代表性的著名互斥量和全局同享變量這兩種辦法停止引見:
// 創立互斥量
HANDLE m_hMutex = CreateMutex(NULL, FALSE, "Sample07");
// 檢討毛病代碼
if (GetLastError() == ERROR_ALREADY_EXISTS) {
 // 假如已有互斥量存在則釋放句柄並復位互斥量
 CloseHandle(m_hMutex);
 m_hMutex = NULL;
 // 法式加入
 return FALSE;
}
  下面這段代碼演示了著名互斥量在過程互斥中的用法。代碼的焦點是CreateMutex()對著名互斥量的創立。CreateMutex()函數可用來創立一個著名或無名的互斥量對象,其函數原型為:
HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向平安屬性的指針
 BOOL bInitialOwner, // 初始化互斥對象的一切者
 LPCTSTR lpName // 指向互斥對象名的指針
);
  假如函數勝利履行,將前往一個互斥量對象的句柄。假如在CreateMutex()履行前曾經存在有雷同名字的互斥量,函數將前往這個曾經存在互斥量的句柄,而且可以經由過程GetLastError()獲得毛病代碼ERROR_ALREADY_EXIST。可見,經由過程對毛病代碼ERROR_ALREADY_EXIST的檢測可以完成CreateMutex()對過程的互斥。

  應用全局同享變量的辦法則重要是在MFC框架法式中經由過程編譯器來完成的。經由過程#pragma data_seg預編譯指令創立一個新節,在此節中可用volatile症結字界說一個變量,並且必需對其停止初始化。Volatile症結字指定了變量可認為內部過程拜訪。最初,為了使該變量可以或許在過程互斥進程中施展感化,還要將其設置為同享變量,同時許可具有讀、寫拜訪權限。這可以經由過程#pragma comment預編譯指令來告訴編譯器。上面給出應用了全局變量的過程互斥代碼清單:
#pragma data_seg("Shared")
int volatile g_lAppInstance =0;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,RWS")
……
if(++g_lAppInstance>1)
return FALSE;
  此段代碼的感化是在過程啟動時對全局同享變量g_nAppInstancd 加1 ,假如發明其值年夜於1,那末就前往FALSE以告訴過程停止。這裡須要特殊指出的是,為了使以上兩段代碼可以或許真正起到對過程互斥的感化,必需將其放置在運用法式的進口代碼處,即運用法式類的初始化實例函數InitInstance()的開端處。

停止過程

  過程只是供給了一段地址空間和內查對象,其運轉是經由過程在其地址空間內的主線程來表現的。當主線程的進入點函數前往時,過程也就隨之停止。這類過程的終止方法是過程的正常加入,過程中的一切線程資本都可以或許獲得准確的消除。除這類過程的正常推出方法外,有時還須要在法式中經由過程代碼來強迫停止本過程或其他過程的運轉。ExitProcess()函數便可在過程中的某個線程中應用,並將立刻終止本過程的運轉。ExitProcess()函數原型為:
VOID ExitProcess(UINT uExitCode);

  其參數uExitCode為過程設置了加入代碼。該函數具有強迫性,在履行終了落後程即曾經被停止,是以位於厥後的任何代碼將不克不及被履行。固然ExitProcess()函數可以在停止過程的同時告訴與其相干聯的靜態鏈接庫,然則因為它的這類履行的強迫性,使得ExitProcess()函數在應用大將存在有平安隱患。例如,假如在法式挪用ExitProcess()函數之前曾用new操作符請求過一段內存,那末將會因為ExitProcess()函數的強迫性而沒法經由過程delete操作符將其釋放,從而形成內存洩露。有鑒於ExitProcess()函數的強迫性和不平安性,在應用時必定要惹起留意。

  ExitProcess()只能強迫履行本過程的加入,假如要在一個過程中強迫停止其他的過程就要用TerminateProcess()來完成。與ExitProcess()分歧,TerminateProcess()函數履行後,被終止的過程是不會獲得任何干於法式加入的告訴的。也就是說,被終止的過程是沒法在停止運轉進步行加入前的掃尾任務的。所以,平日只要在其他任何辦法都沒法迫使過程加入時才會斟酌應用TerminateProcess()去強迫停止過程的。上面給出TerminateProcess()的函數原型:
BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);
  參數hProcess和uExitCode分離為過程句柄和加入代碼。假如被停止的是本過程,可以經由過程GetCurrentProcess()獲得到句柄。TerminateProcess()是異步履行的,在挪用前往後其實不能肯定被終止過程能否曾經真的加入,假如挪用TerminateProcess()的過程對此細節關懷,可以經由過程WaitForSingleObject()來期待過程的真正停止。

小結

  多過程是多義務治理中的主要內容,文中上述部門對其根本概念和重要的技巧如子過程的創立與停止、過程間的互斥運轉等做了較具體的引見。經由過程本文讀者應能對多過程治理有一個初步的熟悉。

願望本文所述對年夜家的VC++法式設計有所贊助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved