• 多线程笔记3


    第四章:同步控制

    1.理解同步与异步的概念

    2.Critical Sections:

    (1)critical sections:是指“用来处理一份被共享之资源”的程序代码。如内存、数据结构、文件等。

    (2)critical section并不是核心对象,存在于进程的内存中。

    (3)VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);初始化CRITICAL_SECTION类型变量。

    (4)VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);清除CRITICAL_SECTION类型变量。

    (5)VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);线程进入,锁定lpCriticalSection。

    (6)VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);线程离开,解除锁定。

    (7)一旦线程进入一个 critical section,它就能够一再地重复进入该 critical section。唯一的警告就是,每一个“进入”操作都必须有一个对应的“离开”操作。

    (8)不要长时间锁住一份资源:千万不要在一个 critical section 之中调用 Sleep() 或任何 Wait...() API 函数。

    (9)避免Danling Critical Sections:Windows NT 和 Windows 95 在管理dangling critical sections 时有极大的不同。在 Windows NT 之中,如果一个线程进入某个 critical section 而在未离开的情况下就结束,该 critical section 会被永远锁住。然而在 Windows 95 中,如果发生同样的事情,其他等着要进入该 critical section 的线程,将获准进入。这基本上是一个严重的问题,因为你竟然可以在你的程序处于不稳定状态时进入该 critical section。

    3.死锁

    (1)任何时候当一段代码需要两个(或更多)资源时,都有潜在性的死锁阴影。

    (2)强制将资源锁定,使它们成为“all-or-nothing”,可以阻止死锁的发生。

    (3)哲学家进餐问题:WaitForMutipleObjects,只能用于核心对象,引出了Mutex。

     4.互斥器(Mutexes)

    (1)锁住Mutex比critical section 慢,因为要切换到OS核心态。

    (2)Mutexes 可跨进程使用,Critical section 只能在同一个进程中使用。

    (3)等待一个 mutex 时,可以指定“结束等待”的时间长度。但对于critical section 则不行。

    (4)mutex、critical section相关函数比较:

    InitializeCriticalSection()                   CreateMutex()
                                                          OpenMutex()
    EnterCriticalSection()                        WaitForSingleObject()
                                                          WaitForMultipleObjects()
                                                          MsgWaitForMultipleObjects()
    LeaveCriticalSection()                       ReleaseMutex()
    DeleteCriticalSection()                      CloseHandle()

    (5)创建mutex时指定名称,使其能跨进程使用。mutex名称是全局性的。

    (6)HANDLE CreateMutex(

    LPSECURITY_ATTRIBUTES lpMutexAttributes,   //安全属性,NULL表示使用默认的属性
    BOOL bInitialOwner,                                       //TRUE表示调用这个函数的线程拥有该mutex
    LPCTSTR lpName                                           //mutex名称,不能包含
    );

    (7)“mutex 激发状态”:当没任何线程拥有该 mutex 而且有一个线程正以 Wait...() 等待该 mutex,该mutex 就会短暂地出现激发状态,使 Wait...() 得以返回。

    (8)Bool  ReleaseMutex(Handle hMutex) 释放mutex

    (9)Mutex 的拥有权并非属那个产生它的线程,而是那个最后对此 mutex 进行 Wait...() 操作并且尚未进行 ReleaseMutex() 操作的线程。

    (10)处理被舍弃的互斥器:如果线程拥有一个 mutex 而在结束前没有调用 ReleaseMutex(),mutex 不会被摧毁。取而代之的是,该 mutex会被视为“未被拥有”以及“未被激发”,而下一个等待中的线程会被以WAIT_ABANDONED_0 通知。如果其他线程正以 WaitForMultipleObjects() 等待此 mutex,该函数也会返回,传回值介于 WAIT_ABANDONED_0 和 (WAIT_ABANDONED_0_n +1)之间,其中的 n 是指handle 数组的元素个数。线程可以根据这个值了解到究竟哪一个 mutex 被放弃了。至于WaitForSingleObject() , 则只是传回WAIT_ABANDONED_0。

    (11)CreateMutex() 的第二个参数 bInitialOwner,允许你指定现行线程(current thread)是否立刻拥有即将产生出来的mutex。乍见之下这个参数或许只是提供一种方便性,但事实上它阻止了一种 race condition 的发生,如果没有bInitialOwner,你就必须写下这样的代码:

    HANDLE hMutex = CreateMutex(NULL, FALSE, "Sample Name");
    int result = WaitForSingleObject(hMutex, INFINITE);

    但是这样的安排可能会产生 race condition。

    (12)一个线程拥有了某个mutex,后面的wait…()不会被阻塞。

    5.信号量

    (1)HANDLE CreateSemaphore(

    LPSECURITY_ATTRIBUTES lpAttributes,  //安全属性,默认NULL
    LONG lInitialCount,                               //semaphore的初值,必须大于或等于0,小于等于IMaximumCount
    LONG lMaximumCount,                         //semaphore最大值,同一时间能够锁住semaphore之线程的最大数。
    LPCTSTR lpName                                 //semaphore的名称,NULL表示没有名字
    );

    (2) Semaphore没有拥有权的概念,一个线程可以反复调用 Wait...() 函数以产生新的锁定。

    这和 mutex绝不相同:拥有 mutex 的线程不论再调用多少次 Wait...() 函数,也不会被阻塞住。

    (3)BOOL ReleaseSemaphore(

    HANDLE hSemaphore,             //semaphore的handle
    LONG lReleaseCount,               //semaphore现值的增额,不可以是负值或0
    LPLONG lpPreviousCount          //借此传回semaphore原来的现值
    );

    注意:lpPreviousCount 所传回来的是一个瞬间值。你不可以把lReleaseCount 加上 *lpPreviousCount,就当作是 semaphore 的现值,因为其他线程可能已经改变了 semaphore 的值。

    (4)设定semaphore初值的意义与CreateMutex() 的 bInitialOwner 参数的存在理由是一样的

    6.事件

    (1)Event的唯一目的就是成为激发或未激发状态,这两种状态全由程序控制。

    (2)HANDLE CreateEvent(

    LPSECURITY_ATTRIBUTES lpEventAttributes,    //安全属性,NULL默认
    BOOL bManualReset,//FALSE表示event在变成激发状态之后,自动重置为非激发状态,TRUE表示需要调用ResetEvent才能重置
    BOOL bInitialState,//TRUE表示一开始处于激发状态,FALSE处于非激发状态
    LPCTSTR lpName //event名称
    );

    (3)三个操作函数:

    SetEvent:把event对象设为激发状态

    ResetEvent:把event对象设为非激发状态

    PulseEvent:对应Manual Reset Event:把event设为激发状态,唤醒所有等待线程,然后event恢复为非激发状态。

                      对应Auto Reset Event: 把event设为激发状态,唤醒一个等待线程,然后event恢复为非激发状态

    (4)如果你面对一个 AutoReset event 对象调用 SetEvent() 或 PulseEvent(),而彼时并没有任何线程正在等待。这种情况下这个 event 会被遗失.

    7.Interlocked Variables

    (1)  InterlockedIncrement,加1后与0比较,对应于 AddRef()操作

    (2)  InterlockedDecrement,减1后与0比较,对应于Release()操作

    (3)  InterlockedExchange设定一个新值,传回旧值,多线程安全。

  • 相关阅读:
    启动docker
    hadoop hdfs文件操作
    html_day02
    启动hadoop和关闭hadoop
    ubuntu 启动idea
    html_day01_practice
    day01
    构造函数后加冒号及调用顺序
    三大范式
    mysql解决迁移复制数据库报错 Key或column 过长
  • 原文地址:https://www.cnblogs.com/programmer-wfq/p/4642905.html
Copyright © 2020-2023  润新知