1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

なまこの備忘録その1 -マルチスレッド基礎-

Last updated at Posted at 2024-12-10

はじめに

この記事は、個人的に
「今後これ使うだろうな。」「でも多分忘れるだろうな。」
と感じたことを、雑に書いていく記事になります。
間違っている部分、理解が浅い部分等は、指摘して頂けると大変助かります。

本題

今回は、C++のマルチスレッドの基礎的な部分になります。

main.cpp
#include <thread>
#include <iostream>

//加算する回数
const int COUNT_MAX = 10000;

int main(void)
{
    int ret = 0;

    //count1
    for(int c = 0; c < COUNT_MAX;c++)
    {
        //COUNT_MAX回加算するだけ
        ++ret;
        std::cout << "count1" << ret << std::endl;
    }

    //count2
    for(int c = 0; c < COUNT_MAX;c++)
    {
        //COUNT_MAX回加算するだけ
        ++ret;
        std::cout << "count2" << ret << std::endl;
    }

    std::cout << "return" << ret << std::endl
    
    return 0;
}

count1の加算処理が終了した後に
count2の加算処理が実行される...

このような処理をマルチスレッド化していきます。

thread

まず、C++においてマルチスレッドを使用するには
threadのインクルードが必要になります。

そして、マルチスレッドの宣言には
std::threadを使います。

qiita.cpp
#include <thread>
#include <iostream>

const int COUNT_MAX = 10000;

int main(void)
{
    int ret = 0;

	//マルチスレッド
    //関数オブジェクトを渡す
	std::thread thread1([&]()
     {
         for(int c = 0; c < COUNT_MAX; c++)
         {
             ++ret;
             std::cout << "thread1:" << ret << std::endl;
         }
     });

    //マルチスレッド
    //関数オブジェクトを渡す
    std::thread thread2([&]()
     {
         for(int c = 0; c < COUNT_MAX; c++)
         {
             ++ret;
             std::cout << "thread2:" << ret << std::endl;
         }
     });

    std::cout << "return" << ret << std::endl
    
     return 0;
}

これでマルチスレッドになりました。

std::threadを宣言した際に、ラムダ式を渡していますが
これは、マルチスレッドの内部処理として
関数オブジェクトを渡す必要があるためです。

しかし、いざ実行するとエラーが出てしまいます。

メインスレッドとサブスレッド

マルチスレッドには
メインスレッドサブスレッドが存在しています。

今回のコードで言うと
メインスレッド : main
サブスレッド : thread1,thread2
となります。

これらのスレッドは別々に動いていて、
メインスレッドの状態に関わらず、各サブスレッドは処理を進め、終了します。

MainSubThread.drawio.png

もちろん逆もあり
各サブスレッドが終了していなくても、メインスレッドが終了します

MainSubThreadError.drawio.png

Join

threadのJoin関数とは
そのスレッドが終了するまで待機させる関数です。

qiita.cpp
#include <thread>
#include <iostream>

const int COUNT_MAX = 10000;

//メインスレッド
int main(void)
{
    int ret = 0;

	//サブスレッド1
    //関数オブジェクトを渡す
	std::thread thread1([&]()
     {
         for(int c = 0; c < COUNT_MAX; c++)
         {
             ++ret;
             std::cout << "thread1:" << ret << std::endl;
         }
     });

    //サブスレッド2
    //関数オブジェクトを渡す
    std::thread thread2([&]()
     {
         for(int c = 0; c < COUNT_MAX;c++)
         {
             ++ret;
             std::cout << "thread2:" << ret << std::endl;
         }
     });

    //サブスレッド1が終了するまで待機
    thread1.join();

    //サブスレッド2が終了するまで待機
    thread2.join();

    std::cout << "return" << ret << std::endl

    //メインスレッド終了
     return 0;
}

これで、各サブスレッドが終了した後に、メインスレッドを終了することが出来ます。

MultThreadJoin.drawio.png

これで、先程と同じエラーは起こらないはずです。

Sleep関数を使用し、メインスレッドをブロックする」
という方法も存在します。

次の課題

このコードを実行すると
本来の結果と異なる結果が出てくることがあります。

main.cpp 結果
    //本来の結果
    return20000 

    //本来とは異なる結果の例
    return19998 

本来は

1.変数retを読込
2.加算処理
3.変数retに加算結果を反映

という
123の流れで処理が行われます。

マルチスレッドの場合
処理順が、thread1の123の後、thread2の123ではなく

multThreadRet.drawio.png

thread1の12→thread2の12→thread1の3→thread2の3
などのようになってしまうことがあります。

この時、変数retの値は
thread1の加算処理が反映されないまま、thread2で読み込まれ
thread1から書き込んだ値は、thread2から書き込まれる値によって
上書きされてしまいます

この「別スレッドに割り込まれる」現象は、コードの結果から見ることができます。

正しく値が反映されている例
    //正しく増加している
    thread2:thread1:18374
    18374 
    thread1:18376
別スレッドによって上書きされてしまった例
    //どちらか片方の結果が消失している
    thread1:thread2:19974
    19974
    thread2:19975

そして、上記の結果を得られる保証もないという恐ろしい状態です。

このように、複数のスレッドから自由に書き込みが出来るため
中身のデータの整合性が取れない状態を非スレッドセーフと呼びます。

この問題の解決のためには
変数ret、または加算処理をスレッドセーフにしなくてはいけません。

この方法を次回に書いていきます。

余談

threadは、英語で「糸」を意味しているらしいです。

シングルスレッドを一本の糸
マルチスレッドを複数の糸の束(枝分かれして、また合流する)

という感じで考えると、何かしっくりくるような気がしますね。

次回の備忘録

std::atomicとかstd::mutex

1
1
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?