• Windows提高_2.2第二部分:用户区同步


    第二部分:用户区同步

    同步和互斥

    • 同步:就是按照一定的顺序执行不同的线程

    • 互斥:当一个线程访问某一资源的时候,其它线程不能同时访问

    多线程产生的问题

    #include <stdio.h>
    #include <windows.h>// 全局变量,被不同的线程访问和修改
    int g_Number = 0;
    ​
    DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) 
    {
        // 为 g_Number 自增 100000 次
        for (int i = 0; i < 100000; i++)
            g_Number++;
        return 0;
    }
    DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter) 
    {
        // 为 g_Number 自增 100000 次
        for (int i = 0; i < 100000; i++)
            g_Number++;
        return 0;
    }
    int main() 
    {
        // 创建两个线程
        HANDLE hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL);
        HANDLE hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL);
    ​
        // 等待两个线程执行结束
        WaitForSingleObject(hThread1, -1);
        WaitForSingleObject(hThread2, -1);
    ​
        // 输出修改后的全局变量
        printf("%d", g_Number);
    ​
        return 0;
    }

    产生问题的原因

    mov         eax, dword ptr[Number]
    add         eax, 1
    mov         dword ptr[Number],eax
    ; C语言的单条语句被翻译成了多条汇编代码,二线程的切换可能导致多条代码分开执行
    mov         eax, dword ptr[Number]    [0]: Number(0)  eax(0)
    add         eax, 1                   [0]: Number(0)  eax(1)
    mov         dword ptr[Number],eax     [0]: Number(1)  eax(1)

    mov         eax, dword ptr[Number]   [1]: Number(1)  eax(1)
    add         eax, 1                   [1]: Number(1)  eax(2)
    mov         dword ptr[Number],eax     [1]: Number(2)  eax(1)

    mov         eax, dword ptr[Number]   [0]: Number(2)  eax(2)
    add         eax, 1                   [0]: Number(2)  eax(3) -----------

    mov         eax, dword ptr[Number]   [1]: Number(2)  eax(2)
    add         eax, 1                   [1]: Number(2)  eax(3)
    mov         dword ptr[Number],eax     [1]: Number(3)  eax(3)
    mov         eax, dword ptr[Number]   [1]: Number(3)  eax(3)
    add         eax, 1                   [1]: Number(3)  eax(4)
    mov         dword ptr[Number],eax     [1]: Number(4)  eax(4)

    mov         dword ptr[Number],eax     [0]: Number(3)  eax(3) -----------

    原子操作(Interlocked...)

    • 特点:将一条语句转换成了具有同等功能的单条汇编指令 lock inc dword ptr [Number]

    • 缺点:只能给单个整数类型(4/8)进行保护,不能给使一段代码变成原子操作

    • 函数:

      • InterlockedXXX

    for (int i = 0; i < 100000; i++)
    {
        // 使用原子操作函数,将自增操作变为不可分割的一条指令
        InterlockedIncrement(&g_Number);
    ​
        // 以上语句会被翻译成下列单条汇编指令
        // lock inc dword ptr [g_Number]
    }

    临界区(CriticalSection)

    • 特点:拥有线程拥有者的概念,同一个线程可以不断的重新进入临界区,但是进入了多少次,就要退出多少。

    • 缺点:一旦拥有临界区的线程崩溃,那么所有等待临界区的线程就会产生死锁。

    • 函数:

      • 初始化: InitializeCriticalSection

      • 保护:EnterCriticalSection

      • 结束保护 :LeaveCriticalSection

      • 删除:DeleteCriticalSection

    // 1. 创建一个临界区(关键段)结构体
    CRITICAL_SECTION CriticalSection = { 0 };
    ​
    // 2. 在 【main】 函数中对创建的临界区进行初始化操作
    // InitializeCriticalSection(&CriticalSection);
    ​
    DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
    {
        // 为 g_Number 自增 100000 次
        for (int i = 0; i < 100000; i++)
        {
            // 当有一个线程正在执行代码的时候 
    // 同一个线程每进入一次受保护的区域,RecursionCount +1
            // OwningThread 当前被哪一个线程所有
    // 3. 使用 EnterCriticalSection 标识需要保护的代码的起始位置
            EnterCriticalSection(&CriticalSection);
            g_Number++;
            // 4. 使用 LeaveCriticalSection 标识需要保护的代码的结束位置
            LeaveCriticalSection(&CriticalSection);
        }
        return 0;
    }

     

  • 相关阅读:
    hdu3829(最大独立集)
    hdu2444(判二分图+最大匹配)
    hdu2063+hdu1083(最大匹配数)
    hdu3622(二分+two-sat)
    poj3678(two-sat)
    hdu1824(two-sat)
    hdu3062(two-sat)
    POJ1067 取石子游戏
    POJ1066 Treasure Hunt
    POJ1065 Wooden Sticks
  • 原文地址:https://www.cnblogs.com/ltyandy/p/10938192.html
Copyright © 2020-2023  润新知