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[编辑]

下一节[编辑]