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

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

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

```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**を使います。

```c++: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を宣言した際に、ラムダ式を渡していますが
これは、マルチスレッドの内部処理として
**関数オブジェクト**を渡す必要があるためです。

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


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

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

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

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

![MainSubThread.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940568/ea407071-5aea-dbbc-b11d-5ce10b1e2427.png)


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

![MainSubThreadError.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940568/bfe5a751-1655-e1f4-b3f8-41656593723f.png)



# Join

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

```c++: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940568/f1ae6b30-4aae-10a3-77c6-48011b27d785.png)


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

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

# 次の課題

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

```c++ : main.cpp 結果
    //本来の結果
    return20000 

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

本来は

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

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

が

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

![multThreadRet.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940568/4f8eab98-998c-e323-faca-2a53c3b32408.png "MultThreadReturn")

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

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

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

``` c++ : 正しく値が反映されている例
    //正しく増加している
    thread2:thread1:18374
    18374 
    thread1:18376
```
    
``` c++ : 別スレッドによって上書きされてしまった例
    //どちらか片方の結果が消失している
    thread1:thread2:19974
    19974
    thread2:19975
```
~~そして、上記の結果を得られる保証もないという恐ろしい状態です。~~
:::


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


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

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


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

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

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

# 次回の備忘録
std::atomicとかstd::mutex

https://qiita.com/Qiita_No_Namaco/items/477c4e6893058c7ddec6