跳至內容

Windows Programming/多任務

維基教科書,自由的教學讀本

進程[編輯]

檢查是否有同名的.exe進程[編輯]

有些應用場合,不允許一個.exe被同時有多個進程實例。因此,在啟動一個.exe時,要先判斷沒有同名的.exe已經運行了。示例代碼如下:

PROCESSENTRY32 entry;
HANDLE snapshot;

		snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);
		if( snapshot == INVALID_HANDLE_VALUE )
		{
			TRACE_ERROR(TRUE,"Error CreateToolhelp32Snapshot");
			return FALSE;
		}

		if (Process32First(snapshot, &entry))
		{
			while(Process32Next(snapshot, &entry))
			{
				if (!strcmp(entry.szExeFile,szApplication))  //szApplication不含路径名
				{
///					TRACE_INFO_FORMAT1(FALSE,"MEP software is already running (%s)",szFile);
					CloseHandle(snapshot);
					return TRUE;
				}
			}
		}
		else
		{
			TRACE_ERROR(TRUE,"Error Process32First");
			CloseHandle(snapshot);
			return FALSE;
		}
		CloseHandle(snapshot);

創建進程[編輯]

過程如下:

STARTUPINFO stStart = { sizeof(si) };
PROCESS_INFORMATION stInfo;
                GetStartupInfo( &stStart );
                if (!CreateProcess(szApplicationFile,NULL,NULL,NULL,FALSE,0,NULL,szCurrentDirectory,&stStart,&stInfo))
		{
			TRACE_ERROR_FORMAT1(TRUE,"Error while launching MEP software (%s)",szFile);
			return FALSE;
		}
		else
		{
			TRACE_INFO_FORMAT1(FALSE,"MEP software launched (%s)",szFile);
			return TRUE;
		}

其中,dwCreationFlags用於標識標誌,以便用於規定如何來創建新進程。可以取值為:

  • EBUG_PROCESS 父進程想要調試子進程和子進程將來生成的任何進程。當任何子進程(被調試進程)中發生某些事件時,將情況通知父進程。
  • DEBUG_ONLY_THIS_PROCESS 與DEBUG_PROCESS標誌相類似,調試程序只被告知緊靠父進程的子進程中發生的特定事件。
  • CREATE_SUSPENDED 新進程被創建,但是,它的主線程則被掛起。
  • DETACHED_PROCESS 阻止基於CUI的進程對它的父進程的控制台窗口的訪問,並告訴系統將它的輸出發送到新的控制台窗口。
  • CREATE_NEW_CONSOLE 為新進程創建一個新控制台窗口。如果同時設定CREATE_NEW_CONSOLE和DETACHED_PROCESS標誌,就會產生一個錯誤。
  • CREATE_NO_WINDOW 不為應用程式創建任何控制台窗口。
  • CREATE_NEW_PROCESS_GROUP 修改用戶在按下Ctrl+C或Ctrl+Break鍵時得到通知的進程列表。
  • CREATE_DEFAULT_ERROR_MODE 不繼承父進程使用的錯誤模式。
  • CREATE_SEPARATE_WOW_VDM 只能當你在Windows2000上運行16位Windows應用程式時使用。告訴系統創建一個單獨的DOS虛擬機(VDM),並且在該VDM中運行16位Windows應用程式。
  • CREATE_SHARED_WOW_VDM 只能當你在Windows2000上運行16位Windows應用程式時使用。在系統的共享VDM中運行16位Windows應用程式。
  • CREATE_UNICODE_ENVIRONMENT 告訴系統,子進程的環境塊應該包含Unicode字符。按照默認設置,進程的環境塊包含的是ANSI字符串。
  • CREATE_FORCEDOS 強制系統運行嵌入16位OS/2應用程式的MOS-DOS應用程式。
  • CREATE_BREAKAWAY_FROM_JOB 使作業中的進程生成一個與作業相關聯的新進程
  • IDLE_PRIORITY_CLASS、BELOW_NORMAL_PRIORITY_CLASS、NORMAL_PRIORITY_CLASS、ABOVE_NORMAL_PRIORITY_CLASS、HIGH_PRIORITY_CLASS、REALTIME_PRIORITY_CLASS 空閒、低於正常、正常、高於正常、高實時,對於大多數應用程式來說不應該設定優先級類。

對於結構:

typedef struct _STARTUPINFO
{
    DWORD cb;            //包含STARTUPINFO结构中的字节数.如果Microsoft将来扩展该结构,它可用作版本控制手段.应用程序必须将cb初始化为sizeof ( STARTUPINFO )
    PSTR lpReserved;      //保留。必须初始化为N U L L
    PSTR lpDesktop;    //用于标识启动应用程序所在的桌面的名字。如果该桌面存在,新进程便与指定的桌面相关联。如果桌面不存在,便创建一个带有默认属性的桌面,并使用为新进程指定的名字。    如果lpDesktop是NULL(这是最常见的情况 ),那么该进程将与当前桌面相关联
    PSTR lpTitle;    //用于设定控制台窗口的名称。如果l p Ti t l e 是N U L L ,则可执行文件的名字将用作窗口名
    DWORD dwX;       //用于设定应用程序窗口在屏幕上应该放置的位置的x 和y 坐标(以像素为单位)。
    DWORD dwY;       //只有当子进程用CW_USEDEFAULT作为CreateWindow的x参数来创建它的第一个重叠窗口时,    才使用这两个坐标。若是创建控制台窗口的应用程序,这些成员用于指明控制台窗口的左上角

    DWORD dwXSize;  //用于设定应用程序窗口的宽度和长度(以像素为单位)
    DWORD dwYSize;  // 只有当子进程将CW_USEDEFAULT 用作CreateWindow 的nWidth参数来创建它的第一个重叠窗口时,才使用这些值。若是创建控制台窗口的应用程序,这些成员将用于指明控制台窗口的宽度
    DWORD dwXCountChars;  //用于设定子应用程序的控制台窗口的宽度和高度(以字符为单位)
    DWORD dwYCountChars;
    DWORD dwFillAttribute;   //用于设定子应用程序的控制台窗口使用的文本和背景颜色
    DWORD dwFlags;           //请参见下一段的说明
    WORD wShowWindow;        //用于设定如果子应用程序初次调用的ShowWindow 将SW_SHOWDEFAULT 作为    nCmdShow 参数传递时,该应用程序的第一个重叠窗口应该如何出现。本成员可以是通常用于ShowWindow 函数的任何一个SW_*标识符
    WORD cbReserved2;        //保留。必须被初始化为0
    PBYTE lpReserved2;       //保留。必须被初始化为NULL
    HANDLE hStdInput;        //用于设定供控制台输入和输出用的缓存的句柄。按照默认设置,hStdInput 用于标识键盘缓存,hStdOutput 和hStdError用于标识控制台窗口的缓存
    HANDLE hStdOutput;
    HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;

dwFlags可以取值:

  • STARTF_USESIZE // 使用dwXSize 和dwYSize 成員
  • STARTF_USESHOWWINDOW //使用wShowWindow 成員
  • STARTF_USEPOSITION //使用dwX 和dwY 成員
  • STARTF_USECOUNTCHARS //使用dwXCountChars 和dwYCount Chars 成員
  • STARTF_USEFILLATTRIBUTE //使用dwFillAttribute 成員
  • STARTF_USESTDHANDLES //使用hStdInput 、hStdOutput 和hStdError 成員
  • STARTF_RUN_FULLSCREEN //強制在x86 計算機上運行的控制台應用程式以全屏幕方式啟動運行
  • STARTF_FORCEONFEEDBACK
  • STARTF_FORCEOFFFEEDBACK

作業:管理進程[編輯]

線程[編輯]

線程擁有的用戶對象是窗口與鈎子。

創建線程[編輯]

  • CreateThread——Windows的API函數。應該僅限於工作者線程。線程函數定義為:DWORD WINAPI _yourThreadFun(LPVOID pParameter)。該函數創建的線程的內核對象的引用計數初始值為2,因為該線程與創建該線程時返回的句柄各索引了該內核對象。棧初始化時,壓入兩個參數,即RtlUserThreadStart函數的兩個參數,而這個RtlUserThreadStart函數是從內核態轉入用戶態後該線程第一個被執行的函數。
  • _beginthreadex——MS對C Runtime庫的擴展SDK函數。針對C Runtime庫做了一些初始化的工作,以保證C Runtime庫工作正常;然後,調用CreateThread真正創建線程。 千萬不要使用_beginthread與_endthread。
  • AfxBeginThread——MFC中創建GUI線程的MFC全局函數。首先創建了相應的CWinThread對象,然後調用CWinThread::CreateThread,在CWinThread::CreateThread中,完成了對線程對象的初始化工作,然後,調用_beginthreadex創建線程。簡化了操作或讓線程能夠響應消息,即可用於界面線程,也可以用於工作者線程。線程函數定義為:UINT _yourThreadFun(LPVOID pParam)

傳遞參數[編輯]

引用自己的線程內核對象[編輯]

  • GetCurrentThread():返回偽句柄,即不會增加句柄的引用計數,也不會在當前進程中創建該句柄。
  • DuplicateHandle():把偽句柄(pseudo)轉變為真句柄(real)。使用完後,應該CloseHandle。

結束線程[編輯]

線程結束後,線程內核對象的狀態變為被觸發(signaled),所有等待該線程的線程被觸發。線程終止運行,線程內核對象的引用計數減1,但不會自動釋放除非引用計數為0;因為可能有其他線程在等待該線程內核對象。可以用GetExitCodeThread來查看線程是否退出。

  • 線程函數返回:推薦使用。因此最佳實踐是用一個標誌量表示是否需要結束線程,線程函數每次工作循環開始時判斷此標誌量從而實現完美退出。
  • ExitThread:實際上這是背後缺省使用。顯式調用將不會析構C++對象與資源。清理線程的棧。
  • TerminateThread:直接殺掉線程。異步函數。被殺死線程收不到「被殺」通知,線程不能正確清理資源,也不能阻止自己被殺。不會清理釋放該線程的棧。DLL不能收到線程終止通知。
  • 進程終止:避免使用。因為主線程的入口點返回時,C啟動代碼將調用ExitProcess函數,這導致了所有在運行的子線程被結束;因此應該明確處理好每個子線程的終止。

線程局部存儲[編輯]

優先級[編輯]

調試[編輯]

判斷線程是否已經退出[編輯]

//判断
bool IsThreadExit(HANDLE hThread)
{
    bool bRet = false;
    DWORD dwExitCode;
    if(GetExitCodeThread(hThread, &dwExitCode))
    {
        if(dwExitCode != STILL_ACTIVE)
            bRet = true;
    }
    else
    {
        //error
        err = GetLastError();   
        throw err;
    }
    return bRet; 
}

同步[編輯]

Events[編輯]

Mutexes[編輯]

Critical Sections[編輯]

Spin Locks[編輯]

讀寫鎖[編輯]

主頁面:Windows_Programming/讀寫鎖

Interlocked變量訪問[編輯]

主頁面:Windows_Programming/Interlocked變量訪問

Fiber[編輯]

下一節[編輯]