マルチスレッド同期 - クリティカル セクション (クリティカル セクション)

1 はじめに

クリティカル セクションは、実行前に共有リソースへの排他的アクセスを必要とする小さなコードです。このアプローチにより、複数行のコードで「アトミックな方法」でリソースを操作できます。

使用する機能:

指定されたクリティカル セクション オブジェクトの所有権を待機します。呼び出し元のスレッドに所有権が付与されると、関数は戻ります。

void EnterCriticalSection(
  [in, out] LPCRITICAL_SECTION lpCriticalSection
);

 指定されたクリティカル セクション オブジェクトの所有権を解放します。

void LeaveCriticalSection(
  [in, out] LPCRITICAL_SECTION lpCriticalSection
);

クリティカル セクション オブジェクトを初期化します。EnterCriticalSection を呼び出す前に、初期化関数を呼び出す必要があります。

void InitializeCriticalSection(
  [out] LPCRITICAL_SECTION lpCriticalSection
);

リソースを解放します。 

void DeleteCriticalSection(
  [in, out] LPCRITICAL_SECTION lpCriticalSection
);

2. 例

この例では、2 つのスレッドが作成されます。グローバル変数 g_sum を累積します。

// Interlocked.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>

DWORD WINAPI add0(LPVOID);
DWORD WINAPI add1(LPVOID);

int g_count = 10;
long g_sum = 0;
CRITICAL_SECTION g_cs;

int main()
{
    HANDLE aThread[2];
    DWORD ThreadID;

    InitializeCriticalSection(&g_cs);

    aThread[0] = CreateThread(
        NULL,       // default security attributes
        0,          // default stack size
        (LPTHREAD_START_ROUTINE)add0,
        NULL,       // no thread function arguments
        0,          // default creation flags
        &ThreadID); // receive thread identifier

    if (aThread[0] == NULL)
    {
        printf("CreateThread error: %d\n", GetLastError());
        return 1;
    }


    aThread[1] = CreateThread(
        NULL,       // default security attributes
        0,          // default stack size
        (LPTHREAD_START_ROUTINE)add1,
        NULL,       // no thread function arguments
        0,          // default creation flags
        &ThreadID); // receive thread identifier

    if (aThread[1] == NULL)
    {
        printf("CreateThread error: %d\n", GetLastError());
        return 1;
    }


    WaitForMultipleObjects(2, aThread, TRUE, INFINITE);

    // Close thread and mutex handles

    for (int i = 0; i < 2; i++)
        CloseHandle(aThread[i]);

    DeleteCriticalSection(&g_cs);

    return 0;

}

DWORD WINAPI add0(LPVOID lpParam)
{
    UNREFERENCED_PARAMETER(lpParam);

    EnterCriticalSection(&g_cs);
    g_sum = 0;

    for (int i = 0; i < g_count; i++)
    {
        g_sum += i;
    }

    printf("Thread %d g_sum = %d\n",
        GetCurrentThreadId(), g_sum);
   
    LeaveCriticalSection(&g_cs);

    return g_sum;
}

DWORD WINAPI add1(LPVOID lpParam)
{
    UNREFERENCED_PARAMETER(lpParam);

    EnterCriticalSection(&g_cs);
    g_sum = 0;

    for (int i = 0; i < g_count; i++)
    {
        g_sum += i;
    }

    printf("Thread %d g_sum = %d\n",
        GetCurrentThreadId(), g_sum);

    LeaveCriticalSection(&g_cs);

    return g_sum;
}

まず、CRITICAL_SECTION g_cs データ構造を定義する必要があります。次に、共通リソース (g_sum) にアクセスする必要があるコードをEnterCriticalSection と LeaveCriticalSection の間に配置します。着信 g_cs がアドレスであることに注意してください。

Interlocked 機能を使用して同期の問題を解決できない場合は、クリティカル セクションを試してください。その最大の利点は、非常に使いやすいことと、Interlocked 機能を内部で使用していることですが、最大の欠点は、複数のプロセス間でスレッドを同期するために使用できないことです。

3. ミューテックス カーネル オブジェクト

マルチスレッド同期 - ミューテックス カーネル オブジェクト_codeee さんのブログ - CSDN ブログミューテックス カーネル オブジェクトは、スレッドがリソースに排他的にアクセスできるようにするために使用されます。使用量です。スレッド ID (システム内のどのスレッドが現在ミューテックスを所有しているかを識別するために使用されます)。再帰カウンター (このスレッドがミューテックスを所有する回数を示すために使用されます)。スレッド ID が 0 (無効な ID) の場合、mutex はどのスレッドにも所有されておらず、トリガー状態にあります。ID がゼロ以外の数値の場合、スレッドはミューテックスを所有しており、トリガーされていない状態にあります。他のすべてのカーネル オブジェクトとは異なり、オペレーティング システムはミューテックスを特別に扱い、いくつかの一般的な規則に違反することを許可します。ミューテックスを作成します。); psa: SECURITY_ATTRIBUTES 構造体へのポインター。https://blog.csdn.net/wzz953200463/article/details/127309051?spm=1001.2014.3001.5501

4. ミューテックス オブジェクトとキー セグメントの類似点と相違点

ミューテックス オブジェクトの動作特性はクリティカル コード セグメントと同じですが、ミューテックス オブジェクトはカーネル オブジェクトに属し、クリティカル コード セグメントはユーザー モード オブジェクトに属します。これは、ミューテックスがコードのクリティカル セクションよりも遅く実行されることを意味します。ただし、これは、異なるプロセスの複数のスレッドが 1 つのミューテックス オブジェクトにアクセスできることも意味します。また、スレッドがリソースへのアクセスを待機している間にタイムアウト値を設定できることも意味します。

特性 ミューテックス オブジェクト キーセグメント
走行速度 遅い 素早い
プロセスの境界を越えて使用できますか はい いいえ
声明 ハンドル hmtx; CRITICAL_SECTION cs;
初期化 hmtx = CreateMutex(NULL,FALSE,NULL); InitializeCriticalSection(&es);
クリアする CloseHandle(hmtx); DeleteCriticalSection(&cs);
無限に待つ WaitForSingleObject(hmtx ,INFINITE); EnterCriticalSection(&cs);
0 待機中 WaitForSingleObject(hmtx , 0); EnterCriticalSection(&cs);
任意待機 WaitForSingleObject(hmtx,dwMilliseconds); できない
解放された リリースミューテックス(hmtx); LeaveCriticalSection(&cs);
他のカーネルオブジェクトを待つことは可能ですか? はい (WaitForMultipleObjects または同様の関数を使用)     
いいえ

おすすめ

転載: blog.csdn.net/wzz953200463/article/details/127396867