Task を起動

Task を使うと CPU が平行して動きます。

前田稔の超初心者のプログラム入門

プログラムの説明

  1. 最近では複数の CPU を備えたパソコンが主流です。
    Task を使うと複数の CPU が平行して動きます。
    このプロジェクトは Windows10 & Visual Studio Community 2015 の環境で Win32 API を使って開発しています。
    環境が違うと動かないことがあるので留意して下さい。
    Visual Studio Community 2015 を起動して、[ファイル][新規作成][プロジェクト][Visual C++]から[Win32][Win32 コンソールアプリケーション]を選んで下さい。
    または、Win32 Console Mode のプロジェクトをコピーして、[プロジェクト][Main のプロパティ]から[Unicide 文字セットを使用する] に設定して下さい。
  2. Main.cpp を次のように修正します。
    #include <ppltasks.h>
    #include <string>
    #include <windows.h>
    
    using namespace concurrency;
    using namespace std;
    
    int wmain()
    {
        auto t = create_task([]()
        {
            DWORD   ms;
            wchar_t str[40];
            for (int i = 0; i < 3; i++)
            {
                ms = GetTickCount();        // Win32 API()
                swprintf(str, 40, L"A: Time %d ミリ秒\n", ms);
                OutputDebugString(str);
                Sleep(rand() % 100);
            }
        });
        t.then([]()
        {   OutputDebugString(L"Run Task\n");
        }).wait();
    }
    
  3. auto t = create_task(・・・) でタスクを生成します。
    (・・・)が生成されるタスクですが、今回は内部関数の形になっています。
  4. 内部関数では Win32 API 関数 GetTickCount(); を使ってタイムスタンプをミリ秒で取得して印字します。
    関数で返される値は DWORD ms であることに注目してください。
    戻り値は 0 ミリ秒から 2^32 ミリ秒(約 49.71 日)の間を循環します。
    印字は Sleep(rand() % 100); で時間を置いて、3回行われます。
    rand() は int 型の乱数で rand() % 100 で0~99の乱数を求めています。
  5. t.then([]() でタスクが起動され、.wait(); で終了を待ち合せます。
    L"Run Task\n" はタスクが終了したときに印字されます。
  6. 実行結果です。
    今回はタスクが一個だけなので、通常のプログラムと違いはありません。
    A: Time 85645875 ミリ秒
    A: Time 85645906 ミリ秒
    A: Time 85646375 ミリ秒
    Run Task
    

カスケードで起動

  1. 次は2個のタスクをカスケード(直列)で起動してみます。
    wmain() 関数を次のように修正します。
    auto t = create_task(・・・) で最初のループが終わると return create_task(・・・) で2個目のタスクを生成します。
    最初のループでは A: Time で、2個目のループでは B: Time で印字しています。
    int wmain()
    {
        auto t = create_task([]()
        {
            DWORD   ms;
            wchar_t str[40];
            for (int i = 0; i < 3; i++)
            {
                ms = GetTickCount();        // Win32 API()
                swprintf(str, 80, L"A: Time %d ミリ秒\n", ms);
                OutputDebugString(str);
                Sleep(rand() % 1000);
            }
            return create_task([]()
            {
                DWORD   ms;
                wchar_t str[40];
                for (int i = 0; i < 3; i++)
                {
                    ms = GetTickCount();        // Win32 API()
                    swprintf(str, 80, L"B: Time %d ミリ秒\n", ms);
                    OutputDebugString(str);
                    Sleep(rand() % 1000);
                }
            });
        });
        t.then([]()
        {   OutputDebugString(L"Run Task\n");
        }).wait();
    }
    
  2. カスケードタイプは、先のタスクが終了しないと次のタスクが起動できない時に使われます。
    t.then([]() でタスクが起動され、.wait(); で終了を待ち合せます。
    L"Run Task\n" はタスクが終了したときに印字されます。
  3. 実行結果です。
    A: Time に続いて B: Time が印字されています。
    A: Time 85926562 ミリ秒
    A: Time 85926609 ミリ秒
    A: Time 85927078 ミリ秒
    B: Time 85927406 ミリ秒
    B: Time 85927906 ミリ秒
    B: Time 85928078 ミリ秒
    Run Task
    

平行起動

  1. 先の例ではタスクで実行されている実感はなかったのですが、次は2個のタスクを平行して起動してみます。
    wmain() 関数を次のように修正します。
    auto t と auto t2 を独立して定義します。
    int wmain()
    {
        auto t = create_task([]()
        {
            DWORD   ms;
            wchar_t str[40];
            for (int i = 0; i < 3; i++)
            {
                ms = GetTickCount();        // Win32 API()
                swprintf(str, 80, L"A: Time %d ミリ秒\n", ms);
                OutputDebugString(str);
                Sleep(rand() % 1000);
            }
        });
        auto t2 = create_task([]()
        {
            DWORD   ms;
            wchar_t str[40];
            for (int i = 0; i < 3; i++)
            {
                ms = GetTickCount();        // Win32 API()
                swprintf(str, 80, L"B: Time %d ミリ秒\n", ms);
                OutputDebugString(str);
                Sleep(rand() % 200);
            }
        });
        (t && t2).then([]()
        {   OutputDebugString(L"Run A: B: Task END\n");
        }).wait();
    }
    
  2. (t && t2).then([]() で t と t2 を同時に起動します。
    auto t の Sleep() は rand() % 1000 に、auto t2 の Sleep() は rand() % 200 になっています。
  3. 実行結果です。
    OutputDebugString() が実行された順に A: Time と B: Time が入り混じって印字されています。
    2個のタスクが同時に実行されていることを確認して下さい。
    よく似た機能にスレッドがあります。
    スレッドの例はC#の スレッドを起動 を参照して下さい。
    A: Time 89711031 ミリ秒
    B: Time 89711031 ミリ秒
    B: Time 89711062 ミリ秒
    A: Time 89711062 ミリ秒
    B: Time 89711140 ミリ秒
    A: Time 89711531 ミリ秒
    Run A: B: Task END
    

[Next Chapter ↓] Task の関数値

超初心者のプログラム入門(C/C++)