しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

Thread Safe な Interlocked Singly Linked Lists を使ってみる

iislua でスレッドセーフなリストが必要になったので調べてみると、Windows API には Interlocked Singly Linked Lists (SList) というのが用意されていることを知りました。MSDN にちゃんと解説がありました。

Interlocked Singly Linked Lists (Windows)

リストと言っても API 的にはスタックとして使うような形になっています。

ノンブロッキングなアルゴリズムで SList は実装されているようです。MSDN よりも以下のページの解説の方が分かりやすい気がするので紹介しておきます。パフォーマンスの比較もあります。

やはり Windows にネイティブ実装されているので、パフォーマンスは優れているようです。

一応 MSDN には簡単なサンプルコードも用意されています。

Using Singly Linked Lists (Windows)

サンプルコードが地味に分かりにくかったので、もっとシンプルにしたものを用意しておきました。

// 先頭に必ず SLIST_ENTRY が必要
typedef struct _SLIST_ITEM {
    SLIST_ENTRY ItemEntry;
    int Value;
} SLIST_ITEM, *PSLIST_ITEM;

int main()
{
    // SList を初期化
    auto pListHead = static_cast<PSLIST_HEADER>(_aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT));

    InitializeSListHead(pListHead);

    for (int i = 1; i <= 5; i += 1)
    {
        // SList にアイテムを追加する
        auto pListItem = static_cast<PSLIST_ITEM>(_aligned_malloc(sizeof(SLIST_ITEM), MEMORY_ALLOCATION_ALIGNMENT));

        pListItem->Value = i;

        InterlockedPushEntrySList(pListHead, &pListItem->ItemEntry);

        printf("Push Value is %d\n", pListItem->Value);
    }

    printf("=====================\n");

    while (true)
    {
        // SList からアイテムを取り出す
        auto pListEntry = InterlockedPopEntrySList(pListHead);

        if (pListEntry == nullptr)
        {
            printf("List is empty.\n");
            break;
        }

        auto pListItem = reinterpret_cast<PSLIST_ITEM>(pListEntry);

        printf("Pop Value is %d\n", pListItem->Value);

        _aligned_free(pListEntry);
    }

    _aligned_free(pListHead);

    return 1;
}

このサンプルコードを実行すると以下のようになります。スタックとして動作していることが分かります。

f:id:shiba-yan:20160118214909p:plain

注意点としては SList で使うメモリは全て 8/16 バイトでアライメントされたものが必要になるみたいです。なので new などを使うのではなく _aligned_malloc で確保する必要があります。

複数スレッドでアクセスした時の挙動も確認しようと思ったんですが、ここまで書いてめんどくさくなったのでドキュメントを信じることにします。