zoukankan      html  css  js  c++  java
  • 进程和线程

    进程和线程基础知识

    在我们开始讨论线程、进程、时间切片和所有其他精彩的 "调度" 概念之前, 让我们建立一个类比。

    首先, 我要说明线程和进程是如何工作的。

    我能想到的最好的方法 (不深入研究实时系统的设计) 就是在某种情况下想象我们的线程和进程。

    一个进程就比作一个房子

    让我们用一个常规的、日常的对象--一栋房子--来模拟进程和线程的类比。

    房子真的是一个容器, 具有一定的属性 (如建筑面积、卧室数等)。
    如果你这样看, 房子真的不主动做任何事情, 它是一个被动的对象。这是一个很有效的过程。我们很快就会探讨这一点。

    房子的占用者就比作线程

    住在房子里的人是活跃的对象--他们是那些使用各种房间、看电视、做饭、洗澡等等。我们很快就会看到线程的行为方式。

    单线程

    如果你曾经独自生活过, 那么你就知道这是什么样子了--你知道你可以随时在家里做任何你想做的事情, 因为房子里没有其他人。

    如果你想打开立体声, 用洗手间, 吃晚饭--不管什么--你只管做就行了。

    多线程

    当你在房子里加入另一个人时, 事情会发生巨大的变化。比方说, 你结婚了, 所以现在你有一个配偶也住在那里。

    你不能只是在任何特定的点进入洗手间;你需要先检查一下, 确保你的配偶不在那里!
    如果你有两个负责任的成年人住在一所房子里, 一般来说你可以在 "安全" 方面有相当的松懈。

    你知道, 其他成年人会尊重你的空间, 不会试图放火烧厨房 (故意!), 等等。

    现在, 把几个孩子扔进混合, 突然的事情变得更有趣。

    返回到进程和线程

    就像房子占据了房地产领域一样, 一个过程占据了记忆。

    正如一个房子的住户可以自由进入任何他们想要的房间, 一个进程的线程都有共同的访问内存。

    如果进程分配的东西 (妈妈出去买了一个游戏), 所有其他线程立即可以访问它 (因为它存在于公共地址空间-它在家里)。

    同样, 如果进程分配内存, 则该新内存也可用于所有线程。这里的诀窍是识别内存是否应用于进程中的所有线程。

    如果是, 则需要让所有线程同步访问它。如果不是, 那么我们将假定它是特定于特定线程的。

    在这种情况下, 因为只有该线程能够访问它, 我们可以假设不需要同步-线程不会自己绊倒!

    正如我们从日常生活中知道的, 事情并不那么简单。

    现在我们已经看到了基本特性 (摘要: 所有的东西都是共享的), 让我们来看看事情变得更有趣的地方, 以及为什么。

    互斥

    如果你想洗澡, 有人已经在使用卫生间, 你必须等待。线程如何处理此操作?
    这是被称为相互排斥的东西做的。这意味着你的想法-许多线程是互斥的, 当涉及到一个特定的资源。
    如果你在洗澡, 你想有独家访问的浴室。要做到这一点, 你通常会进入浴室, 并锁定从内部的门。

    任何想使用洗手间的人都会被锁堵住。当你完成后, 你会打开门, 允许别人进入。这正是线程所做的。

    线程使用一个名为互斥体(mutex : MUTual EXclusion)的对象。

    此对象类似于门上的锁--一旦线程锁定了互斥锁, 其他线程就无法获取互斥锁, 直到拥有的线程释放 (解锁) 它为止。

    就像门锁一样, 等待获取互斥体的线程将被禁止。
    互斥锁和门锁上出现的另一个有趣的并行现象是, 互斥锁实际上是一个 "咨询" 锁定。

    如果线程不遵守使用互斥体的约定, 则保护是无用的。在我们家的比喻中, 这就像是有人闯进洗手间, 穿过一堵墙, 忽略了门和锁的约定。

    优先级

    如果卫生间现在被锁上了, 还有很多人在等着用呢?显然, 所有的人都坐在外面, 等着谁在浴室里下车。

    真正的问题是: "门打开后会发生什么?谁去下一个?
    你会认为, 让等待最长的人去下一个是 "公平的"。或者是 "公平的", 让谁是最古老的去下一个。或最高。或者最重要的。

    有许多方法来确定什么是 "公平"。我们通过两个因素来解决这个问题: 优先级和等待时间。

    假设两个人同时出现在 (上锁的) 浴室门上。其中一人有一个紧迫的期限 (他们已经开会迟到了), 而另一个没有。

    让那个有紧迫期限的人去下一任是没有意义的吗?当然会的唯一的问题是你如何决定谁更 "重要"。

    在众议院中有紧迫期限的人将得到更高的优先地位, 而那些不被给予优先地位的人们将会受到更大的重视。

    线程也是一样。线程从其父线程继承其调度算法, 但可以调用 pthread_setschedparam() 更改其调度策略和优先级。

    如果有许多线程正在等待, 并且互斥锁将被解除锁定, 则我们会将互斥锁放在具有最高优先级的等待线程上。

    但是, 假设两个人都有同样的优先权。现在你做什么?那么, 在这种情况下, 这将是 "公平的", 让等待最长的人下一个去。

    这不仅是 "公平的", 而且也是中微子内核所做的。在一堆线程等待的情况下, 我们主要是优先级的, 其次是等待的长度。
    互斥锁肯定不是我们遇到的唯一同步对象。让我们看看其它的。

    信号灯

    让我们从浴室搬到厨房, 因为这是一个可以接受的地方:有一个以上的人在同一时间。在厨房, 你可能不想让每个人都同时出现在哪里。

    事实上, 你可能想限制你可以在厨房里的人数 (太多的厨师, 以及所有这些)。

    让我们说, 你从来没有想过有两个以上的人同时在那里。你能用互斥体做吗?

    这实际上是一个非常有趣的问题对于我们的比喻来说。让我们把它分解成几步。

    计数为1的信号量

    卫生间可以有两种情况之一, 两个状态相互缠绕在一起:

    门没有上锁, 房间里没有人。门是锁着的, 一个人在房间里。

    这是一个计数为一的信号量的示例--在该房间中最多只能有一个人, 或者使用信号量的一个线程。

    这里的关键 (双关) 是我们描述锁的方式。在典型的浴室锁中, 您只能从内部锁定和解锁-没有外部可访问的密钥。

    实际上, 这意味着互斥体的所有权是一个原子操作:当你在获取互斥体的过程中, 一些其他线程是不可能得到它的。

    结果是, 只有你拥有互斥体。只能你自己呆在厨房里面。
    我们厨房需要的是一种不同类型的锁。

    计数大于1的信号量

    假设我们在厨房安装了传统的基于钥匙的锁。这个锁的工作方式是, 如果你有一把钥匙, 你可以打开门, 进入。

    任何使用此锁的人都同意, 当他们进去时, 他们会立即把门从里面锁上, 以便外面的任何人进去都需要钥匙。
    好吧, 现在它变成了一个简单的问题, 以控制多少人, 我们要在厨房-挂两个门外的钥匙!厨房总是锁着的。

    当有人想进入厨房时, 他们会发现门外是否有钥匙。如果是这样, 他们带着它, 打开厨房门, 进去, 用钥匙锁上门。
    由于进入厨房的人必须有钥匙, 当他们在厨房里, 我们直接控制允许进入厨房的人的数量通过限制在门外可用的钥匙数量。
    使用线程, 这是通过信号量完成的。"普通" 信号与互斥体一样, 您要么拥有互斥锁, 要么就可以访问该资源, 否则就无法访问。

    我们刚才用厨房描述的信号量是一个计数信号量-它跟踪计数 (由线程可用的键数)。
    作为互斥体的信号量

    我们问了一个问题: "你能用一个互斥体做吗?" 关于用计数实现锁, 答案是 "否"。另一条路怎么样?我们可以使用信号量作为互斥体吗?
    是的。事实上, 在某些操作系统中, 这正是他们所做的--他们没有互斥体, 只有信号量!那么, 为什么要为互斥体操心呢?
    要回答这个问题, 看看你的洗手间。你家的建设者是如何实现 "互斥" 的?我猜你没有挂在墙上的钥匙!
    互斥锁是一种 "特殊用途" 信号量。如果希望在特定代码段中运行一个线程, 则互斥锁是迄今为止最有效的实现。

    相关进程函数

    CreateProcess() 介绍:

    功能:创建新进程及其主线程,这个新进程运行指定的可执行文件。

    函数原型:BOOL CreateProcess(
                      LPCSTR   lpApplicationName,  // 要执行的模块的名称,
                      LPSTR   lpCommandLine, // 要执行的命令行。
                      LPSECURITY_ATTRIBUTES  lpProcessAttributes, // 确定是否可以由子进程继承新进程对象的返回句柄。
                      LPSECURITY_ATTRIBUTES  lpThreadAttributes, // 确定子进程是否可以继承新线程对象的返回句柄。
                      BOOL   bInheritHandles, // 指示新进程是否从调用进程处继承了句柄。
                      DWORD  dwCreationFlags, // 控制优先级类和创建进程的标志。
                      LPVOID   lpEnvironment,  // 指向新进程的环境块的指针。
                      LPCSTR  lpCurrentDirectory,  // 进程当前目录的完整路径。
                      LPSTARTUPINFO   lpStartupInfo,  // 决定新进程主窗体如何显示的结构体。
                      LPPROCESS_INFORMATION   lpProcessInformation  // 接收新进程的识别信息的结构体。
                     );

    参数

    lpApplicationName

    该字符串可指定要执行的模块的完整路径和文件名, 也可以指定部分名称。

    该参数可以为 NULL。在这种情况下, 可执行模块的名字必须处于 lpCommandLine 参数最前面并由空格符与后面的字符分开。

    要运行批处理文件, 必须将 lpApplicationName 设置为 cmd.exe, 并将 lpCommandLine 设置为以下参数:

    空格/c空格批处理文件的名称

    lpCommandLine

    该参数可以为 NULL。在这种情况下, 函数使用 lpApplicationName 指向的字符串作为命令行。

    如果 lpApplicationName 和 lpCommandLine 非 NULL: lpApplicationName 指定要执行的模块, lpCommandLine 指定命令行。

    新进程可以使用 GetCommandLine 检索整个命令行。

    SECURITY_ATTRIBUTES 结构体:

    typedef struct _SECURITY_ATTRIBUTES {
      DWORD  nLength;   // 结构体的大小。
      LPVOID lpSecurityDescriptor;  // 安全描叙符,如果为 NULL,则使用默认的。
      BOOL   bInheritHandle; // 安全描述的对象能否被新创建的进程继承
      } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

    bInheritHandles

    如果此参数为 TRUE, 则调用进程中的每个可继承句柄都将由新进程继承。

    如果参数为 FALSE, 则不继承句柄。请注意, 继承句柄与原始句柄具有相同的值和访问权限。

    dwCreationFlags

    控制优先级类和创建进程的标志。如果未指定任何优先级类标志, 则优先级类默认为 NORMAL_PRIORITY_CLASS。

    lpEnvironment

    指向新进程的环境块的指针。如果此参数为空,新进程使用调用进程的环境。

    环境块由以 NULL 结尾的字符串结尾的块组成。每个字符串的形式如下:name=value

    因为等号用作分隔符, 所以不能在环境变量的名称中使用。

    lpCurrentDirectory

    进程当前目录的完整路径。如果此参数为 NULL, 则新进程将具有与调用进程相同的当前驱动器和目录。

    STARTUPINFO 结构体:详细信息

    typedef struct _STARTUPINFO {
      DWORD  cb; // 结构体的大小(字节)。
      LPSTR  lpReserved; // 保留值,必须为 NULL。
      LPSTR  lpDesktop; //用于标识启动应用程序所在的桌面的名字。通常为 NULL。
      LPSTR  lpTitle; // 用于设定控制台窗口的名称。如果为 NULL, 则可执行文件的名称将用作窗口标题。
      DWORD  dwX;  // 某些情况下(和参数 dwFlags相关),此成员是窗口左上角的 x 偏移量。
      DWORD  dwY;  // 某些情况下(和参数 dwFlags相关),此成员是窗口左上角的 y 偏移量。
      DWORD  dwXSize; // 某些情况下(和参数 dwFlags相关),则此成员是窗口的宽度。
      DWORD  dwYSize; // 某些情况下(和参数 dwFlags相关),则此成员是窗口的高度。
      DWORD  dwXCountChars; // 某些情况下(和参数 dwFlags相关),此成员指定屏幕缓冲区宽度。
      DWORD  dwYCountChars; // 某些情况下(和参数 dwFlags相关),此成员指定屏幕缓冲区高度
      DWORD  dwFillAttribute; // 某些情况下(和参数 dwFlags相关),此成员是初始文本和背景颜色。
    DWORD dwFlags; // 确定进程创建窗口时是否使用某些 STARTUPINFO 成员的组标志。 WORD wShowWindow; // 某些情况下(和参数 dwFlags相关),此成员是ShowWindow()的 nCmdShow 参数中的任何值,除了SW_SHOWDEFAULT。 WORD cbReserved2; // 保留供 C 运行时使用;必须为零。 LPBYTE lpReserved2; // 保留供 C 运行时使用;必须为 NULL。 HANDLE hStdInput; // 某些情况下(和参数 dwFlags相关),则此成员是进程的标准输入句柄。 HANDLE hStdOutput; // 某些情况下(和参数 dwFlags相关),则此成员是进程的标准输出句柄。 HANDLE hStdError; // 某些情况下(和参数 dwFlags相关),则此成员是进程的标准错误句柄。 } STARTUPINFO,
    *LPSTARTUPINFO;

    PROCESS_INFORMATION 结构体:

    typedef struct _PROCESS_INFORMATION {
      HANDLE hProcess; // 返回新创建的进程的句柄。
      HANDLE hThread; // 返回可用于标识进程的全局进程标识符。
      DWORD dwProcessId; // 返回新创建进程的主线程的句柄
      DWORD dwThreadId; // 返回可用于标识线程的全局线程标识符。
      } PROCESS_INFORMATION;

    OpenProcess() 介绍:

    功能:打开现有的本地进程对象。

    函数原型:HANDLE OpenProcess(
                      DWORD dwDesiredAccess, // 对进程对象的访问权限。
                      BOOL  bInheritHandle, // 确定由该进程创建的进程是否继承该句柄。
                      DWORD dwProcessId  // 要打开的本地进程的标识符。
                      );

    返回值:如果函数成功, 则返回值是指定进程的打开句柄。如果函数失败, 返回值为 NULL。

     ExitProcess() 介绍:

    功能:结束调用的进程及其所有的线程。

    函数原型:DECLSPEC_NORETURN VOID ExitProcess(
                      UINT uExitCode // 进程和所有线程的退出代码。通常为 0。
                      );

     返回值:无。

    TerminateProcess() 介绍:

    功能:终止指定的进程及其所有线程。

    函数原型:BOOL TerminateProcess(
                      HANDLE hProcess, // 要终止的进程的句柄。
                      UINT   uExitCode // 进程和线程使用的终止代码。
                      );

    返回值:非零表示成功,零表示失败。

    (注意:使用 GetExitCodeThread() 检索进程的退出值)

    CreateRemoteThread() 介绍:

    功能:创建在另一个进程的虚拟地址空间中运行的线程。

    函数原型:HANDLE CreateRemoteThread(
                      HANDLE   hProcess,  // 要在其中创建线程的进程的句柄。
                      LPSECURITY_ATTRIBUTES  lpThreadAttributes, // 同上。
                      SIZE_T   dwStackSize,  // 堆栈的初始大小 (以字节为单位)。如果为 0,则使用默认大小。
                      LPTHREAD_START_ROUTINE  lpStartAddress, // 指向在远程进程中执行的函数地址。
                      LPVOID   lpParameter,  // 指向要传递给线程函数的变量的指针。
                      DWORD   dwCreationFlags,  // 控制线程创建的标志。通常为 0,表示创建线程后立即执行。
                      LPDWORD  lpThreadId  // 指向接收线程标识符的变量的指针。
                      );

    参数 lpStartAddress:

    typedef DWORD (__stdcall *LPTHREAD_START_ROUTINE) (LPVOID lpThreadParameter);

     LPTHREAD_START_ROUTINE 是个函数指针。

    返回值:如果函数成功, 则返回值是新线程的句柄。如果函数失败, 返回值为 NULL。

      

    GetCommandLine() 介绍:

    功能:检索当前进程的命令行字符串。

    函数原型:LPTSTR WINAPI GetCommandLine();

    返回值:指向当前进程的命令行字符串的指针。

    GetCurrentProcessId() 介绍:

    功能:获取当前进程一个唯一的标识符(PID)。

    函数原型:DWORD GetCurrentProcessId();

     返回值:一个调用进程的进程标识符。

    GetModuleHandle() 介绍:

    功能:检索指定模块的模块句柄。该模块必须已由调用进程加载。

    函数原型:HMODULE WINAPI GetModuleHandle(
                      LPCTSTR lpModuleName // 已加载模块 (.dll 或 .exe 文件) 的名称。

                      // 如果此参数为 NULL, GetModuleHandle 将返回用于创建调用进程 (.exe 文件) 的文件的句柄。
                      );

    返回值:如果函数成功, 则返回值是指定模块的句柄。如果函数失败, 返回值为 NULL。

    DEMOCODE:

    #include<stdio.h>
    #include<Windows.h>
    int main(void)
    {
        STARTUPINFO SI;
        PROCESS_INFORMATION PI;
        TCHAR *CmdLine;
        ZeroMemory(&SI, sizeof(SI));  // 将指定的内存块清零。使用结构前清零,而不让结构的成员数值具有不确定性。
        ZeroMemory(&PI, sizeof(PI));
        SI.cb = sizeof(SI);
        SI.dwFlags = STARTF_USESHOWWINDOW;
        SI.wShowWindow = SW_SHOW;                                 //这下面的空格注意了,引号和C之间。
        CreateProcess("C:\Windows\System32\notepad.exe", (LPSTR)" C:\Users\Public\Documents\Text.txt",
            NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &SI, &PI);
        CmdLine = GetCommandLine();
        printf("CmdLine: %s
    ", CmdLine);
    printf("Current ID: %d ",GetCurrentID()); Sleep(
    3000); TerminateProcess(PI.hProcess, 0);
    CloseHandle(PI.hProcess); Sleep(2000);
    ExitProcess();
    return 0; }

    相关线程函数

    CreateThread() 介绍:
    功能:创建要在调用进程的虚拟地址空间内执行的线程。

    函数原型:HANDLE CreateThread(
                      LPSECURITY_ATTRIBUTES   lpThreadAttributes,  // 指向 SECURITY_ATTRIBUTES 结构的指针。通常为 NULL。
                      SIZE_T   dwStackSize,    // 堆栈的初始大小 (以字节为单位)。通常为 0。
                      LPTHREAD_START_ROUTINE  lpStartAddress, // 指向要由线程执行的应用程序定义函数的指针。
                      LPVOID  lpParameter,   // 指向要传递给线程的变量的指针。
                      DWORD  dwCreationFlags,  // 如果为 0,表示即刻运行该线程,如果为 CREATE_SUSPENDED,表示挂起状态。
                      LPDWORD  lpThreadId  // 指向接收线程标识符的变量的指针。
                      );

    参数:
    lpThreadAttributes

    指向 SECURITY_ATTRIBUTES 结构的指针, 它确定返回的句柄是否可以由子进程继承。

    如果 lpThreadAttributes 为 NULL, 则无法继承句柄。

    dwStackSize

    堆栈的初始大小 (以字节为单位)。系统将此值舍入到最近的页面。

    如果此参数为零, 则新线程将使用可执行文件的默认大小。

    lpStartAddress

    指向要由线程执行的应用程序定义函数的指针。此指针表示线程的起始地址。

    lpParameter

    指向要传递给线程的变量的指针。

    dwCreationFlags

    通常情况下为 0。

    lpThreadId

    指向接收线程标识符的变量的指针。如果此参数为 NULL, 则不返回线程标识符。

    返回值:如果函数成功, 则返回值是新线程的句柄。如果函数失败, 返回值为 NULL。

    第三个参数的函数名可以任意命名,但必须有如下的格式:
    DWORD WINAPI ThreadProc(LPVOID lpParam);

    第四个参数就是传递给 ThreadProc 的参数,利用这个参数可以在主线程和二级线程间共享数据。

    如果需要传递多个参数,应该将这些参数封装到一个自定义的结构体中,然后传递一个该结构体的指针给线程函数。

    如果该参数为 NULL, 那么 ThreadProc() 就不用管这个参数,可以把这个当做线程的 main()。

    SetThreadPriority() 介绍:

    功能:设置指定线程的优先级。

    函数原型:BOOL SetThreadPriority(
                      HANDLE hThread,   // 指定线程的句柄。
                      int  nPriority   // 线程的优先级值。
                      );

     
    线程优先级等级
    标志
    优先级值
    1
    idle (最低)
    THREAD_PRIORITY_IDLE
    如果进程优先级为realtime则调整为16,其它情况为1
    2
    LOWEST 低
    THREAD_PRIORITY_LOWEST
    -2(在原有基础上-2)
    3
    BELOW 低于标准
    THREAD_PRIORITY_BELOW_NORMAL
    -1(在原有基础上-1)
    4
    NORMAL(标准)
    THREAD_PRIORITY_NORMAL
    不变(取进程优先级值)
    5
    ABOVE 高于标准
    THREAD_PRIORITY_ABOVE_NORMAL
    +1(在原有基础上+1)
    6
    HIGHEST (高)
    THREAD_PRIORITY_HIGHEST
    +2(在原有基础上+2)
    7
    CRITICAL(最高)
    THREAD_PRIORITY_TIME_CRITICAL
    如果进程优先级为realtime则调整为31,其它情况为15

    返回值:非零表示成功,零表示失败。

    SuspendThread() 介绍:

    功能:挂起指定的线程。

    函数原型:DWORD SuspendThread(
                      HANDLE hThread   // 指定要挂起线程的句柄。
                      );

    返回值:如果函数成功, 则返回值为线程的上一个挂起计数; 否则为 -1。

    ResumeThread() 介绍:

    功能:恢复被挂起的线程。

    函数原型:DWORD ResumeThread(
                      HANDLE hThread  // 指定要恢复线程的句柄。
                      );

    返回值:如果函数成功, 则返回值为线程的上一个挂起计数。否则为 -1。

    ExitThread() 介绍:

    功能:结束线程自身。

    函数原型:VOID ExitThread(

                      DWORD dwExitCode  // 返回调用线程的退出代码。

                      );

    GetExitCodeThread() 介绍:

    功能:检索指定线程的终止状态。

    函数原型:BOOL GetExitCodeThread(
                      HANDLE  hThread,   // 指定线程的句柄。
                      LPDWORD lpExitCode  // 指向要接收线程终止状态的变量的指针。
                      );

    返回值:非零表示成功,零表示失败。

    TerminateThread() 介绍:

    功能:结束任意一个指定线程。

    函数原型:BOOL TerminateThread(

                      HANDLE hThread,  // 指定线程的句柄。

                      DWORD dwExitCode  // 指向要接收线程终止状态的变量的指针。

                      );

    返回值:非零表示成功,零表示失败。

    CreateMutex() 介绍:

    功能:创建一个互斥锁。

    函数原型:HANDLE CreateMutexA(
                      LPSECURITY_ATTRIBUTES lpMutexAttributes,  // 安全属性指针,一般为 NULL。
                      BOOL  bInitialOwner,  // 表示互斥锁创建出来后是否被当前线程持有。
                      LPCSTR  lpName  // 互斥锁的名字,可以为 NULL。
                      ); 

    返回值:如果函数执行成功,则返回互斥锁的句柄,否则为 NULL。

    WaitForSingleObject() 介绍:

    功能:一直等待直到指定的对象处于终止状态或超时间隔已过。

    函数原型:DWORD WaitForSingleObject(
                      HANDLE hHandle,  // 对象的句柄。如果是信号量的句柄,则该函数返回时会将信号量的值减 1。
                      DWORD  dwMilliseconds // 超时间隔 (以毫秒为单位)。若为 INFINITE,则等待到对象结束。
                      );

    返回值:如果函数成功, 则返回值指示导致函数返回的事件。

    返回值如下:

    含义
    WAIT_ABANDONED 当 Handle 为 mutex 时,如果拥有 mutex 的线程在结束时没有释放核心对象会引发此返回值。
    WAIT_OBJECT_0 指定对象的状态已发出信号。此值为 0。
    WAIT_TIMEOUT 超时间隔已过
    WAIT_FAILED 函数失败。

    说明:在 WaitForSingleObject() 和 ReleaseMutex() 之间的是在临界区操作的内容。

    ReleaseMutex() 介绍:

    功能:释放互斥锁,与 WaitForSingleObject() 一起使用。

    函数原型:BOOL WINAPI ReleaseMutex(

                      HANDLE hMutex   // 要释放的互斥锁。
                       );
    返回值:true 表示释放成功,否则释放失败。

    WaitForMultipleObjects() 介绍:

    功能:等待一个内核对象变为已通知状态。

    函数原型:DWORD WaitForMultipleObjects(
                       DWORD nCount,   // 指明等待的内核对象的个数,最多为 64。
                       CONST HANDLE* lpHandles,  // 存放等待的内核对象句柄的数组。
                       BOOL fWaitAll, // 如果为 true,则只有当等待的所有内核对象为已通知状态时函数才返回,

                                                // 否则只要一个内核对象为已通知状态,该函数就返回。

                       DWORD dwMilliseconds   // 同上。
                       );

    返回值:如果函数失败,返回 WAIT_FAILED,如果超时则返回 WAIT_TIMEOUT。

                  如果 fWaitAll 为 true,函数成功则返回 WAIT_OBJECT_0,

                  如果为 false,函数成功则返回值指明是哪个内核对象收到通知。

    InitializeCriticalSection() 介绍:

    功能:初始化一个临界区对象。

    函数原型:void InitializeCriticalSection(

                      LPCRITICAL_SECTION lpCriticalSection  // 临界区对象的指针。
                      );

    返回值:无。

    EnterCriticalSection() 介绍:

    功能:进入临界区。

    函数原型:void EnterCriticalSection(
                      LPCRITICAL_SECTION lpCriticalSection  // 临界区对象的指针。
                       );

    返回值:无。

    说明:在 EnterCriticalSection() 与 LeaveCriticalSection() 之间是在临界区操作的内容。

    LeaveCriticalSection() 介绍:

    功能:离开临界区。

    函数原型:void LeaveCriticalSection(
                      LPCRITICAL_SECTION lpCriticalSection  // 临界区对象的指针。
                      );

    返回值:无。

    DeleteCriticalSection() 介绍:

    功能:释放临界区对象的所有资源。

    函数原型:void DeleteCriticalSection(
                      LPCRITICAL_SECTION lpCriticalSection  // 临界区对象的指针。
                      );

    返回值:无。

    CreateSemaphore() 介绍:

    功能:创建一个信号量。

    函数原型:HANDLE CreateSemaphore(
                      LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针,一般为 NULL。
                      LONG lInitialCount, // 信号量的初始值,0 <= lInitialCount <= lMaximumCount。
                      LONG lMaximumCount, // 信号量的最大值。
                      LPCTSTR lpName // 信号量的名称。
                      );

    返回值:指向信号量的句柄,如果创建的信号量和已有的信号量重名,那么返回已经存在的信号量句柄。

    ReleaseSemaphore() 介绍:

    功能:将指定信号量对象的计数增加指定的量。

    函数原型:BOOL ReleaseSemaphore(
                      HANDLE hSemaphore,  // 信号量对象的句柄。
                      LONG   lReleaseCount, // 信号量对象当前计数要增加的量。该值必须大于零。

                      // 如果指定的数量会导致信号量的计数超过创建信号量时指定的最大计数,则计数不会更改,函数返回false。
                      LPLONG lpPreviousCount // 指向要接收信号量的上一个计数的变量的指针。

                                                                 // 如果不需要上一个计数, 则此参数可以为NULL 。
                      );

    返回值:非零表示成功,零表示失败。

  • 相关阅读:
    在 Windows 上测试 Redis Cluster的集群填坑笔记
    vmware安装黑苹果教程
    微信支付v3发布到iis时的证书问题
    Linux下安装SQL Server 2016(连接篇SQL Server on linux)
    Linux下安装SQL Server 2016(连接篇SQL Server on linux)
    Linux下安装SQL Server 2016(安装篇SQL Server on linux)
    Linux下安装SQL Server 2016(准备篇SQL Server on linux)
    客服端与服务端APP支付宝支付接口联调的那些坑
    ASP.NET MVC]WebAPI应用支持HTTPS的经验总结
    .net平台下C#socket通信(中)
  • 原文地址:https://www.cnblogs.com/M-Anonymous/p/9379509.html
Copyright © 2011-2022 走看看