何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 761
  • 記事 - 18
  • コメント - 35965
  • トラックバック - 222
ニュース
  • IE7以前では、表示がおかしい。div の解釈に問題があるようだ。
    IE8の場合は、「互換」表示を OFF にしてください。
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2011
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

分身というのは、違う気がする。

parallel ディレクティブがあるだけだと、複数のスレッドがそこを実行する。


//#include "stdafx.h"
#define _WIN32_WINNT 0x0500
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <omp.h>
#include <strsafe.h>
int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;
    _tsetlocale(LC_ALL, _T("Japanese_Japan"));
    #pragma omp parallel shared(hStdout) \
        private(msgBuf, cchStringSize, dwChars)
    {
        StringCchPrintf(msgBuf, BUF_SIZE,
            TEXT("スレッド%dで実行\n"), omp_get_thread_num());
        StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
        WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
    }
    return 0;
}
スレッド0で実行
スレッド1で実行

1つのスレッドに1つの実行範囲を割り当てるのが、・・・そういう範囲がありますよという宣言をするのが sections ディレクティブで、実際に範囲を指定するのが section ディレクティブ。


//#include "stdafx.h"
#define _WIN32_WINNT 0x0500
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <omp.h>
#include <strsafe.h>
int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);;
    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;
    _tsetlocale(LC_ALL, _T("Japanese_Japan"));
    #pragma omp parallel shared(hStdout) \
        private(msgBuf, cchStringSize, dwChars)
    {
        #pragma omp sections
        {
            #pragma omp section
            {
                StringCchPrintf(msgBuf, BUF_SIZE,
                    TEXT("セクション1は、%dで1度だけ実行される。\n"),
                    omp_get_thread_num());
                StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
                WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
            }
            #pragma omp section
            {
                StringCchPrintf(msgBuf, BUF_SIZE,
                    TEXT("このセクションの実行は、%dで1度だけ。\n"),
                    omp_get_thread_num());
                StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
                WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
            }
        }
 }
    return 0;
}
セクション1は、0で1度だけ実行される。
このセクションの実行は、1で1度だけ。

当然の疑問。「セクションの数とスレッドの数が違うと、どうなるの?」やって見る。


//#include "stdafx.h"
#define _WIN32_WINNT 0x0500
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <omp.h>
void threadPrint(int num)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;
    if (hStdout == INVALID_HANDLE_VALUE) {
        return;
    }
    #pragma omp parallel sections num_threads(num) \
        shared(hStdout) private(msgBuf, cchStringSize, dwChars)
    {
        #pragma omp section
        {
            StringCchPrintf(msgBuf, BUF_SIZE, TEXT("セクション1[%d]\n"),
                omp_get_thread_num());
            StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
            WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
        }
        #pragma omp section
        {
            StringCchPrintf(msgBuf, BUF_SIZE, TEXT("セクション2[%d]\n"),
                omp_get_thread_num());
            StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
            WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
        }
        #pragma omp section
        {
            StringCchPrintf(msgBuf, BUF_SIZE, TEXT("セクション3[%d]\n"),
                omp_get_thread_num());
            StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
            WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
        }
        #pragma omp section
        {
            StringCchPrintf(msgBuf, BUF_SIZE, TEXT("セクション4[%d]\n"),
                omp_get_thread_num());
            StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
            WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
        }
    }
}
int _tmain(int argc, _TCHAR* argv[])
{
    _tsetlocale(LC_ALL, _T("Japanese_Japan"));
    for (int i = 1; i <= 6; ++i) {
        _tprintf(_T("%d個で呼び出し--------------------\n"), i);
        threadPrint(i);
    }
    return 0;
}
1個で呼び出し--------------------
セクション1[0]
セクション2[0]
セクション3[0]
セクション4[0]
2個で呼び出し--------------------
セクション2[1]
セクション1[0]
セクション4[0]
セクション3[1]
3個で呼び出し--------------------
セクション1[0]
セクション3[2]
セクション2[1]
セクション4[0]
4個で呼び出し--------------------
セクション1[0]
セクション2[1]
セクション3[3]
セクション4[2]
5個で呼び出し--------------------
セクション1[0]
セクション2[1]
セクション3[2]
セクション4[4]
6個で呼び出し--------------------
セクション2[2]
セクション3[4]
セクション1[1]
セクション4[0]

sections ディレクティブを指定すると、コードは section ディレクティブの中に書かれなければならない。ただし、最初の section ディレクティブは、省略が可能。

何回か実行して、スレッドが比較的ばらけた結果を載せた。スレッド数を3個以上にしても、1つのスレッドが全てのセクションを実行することもある。これは、空いているスレッドを使うようなことが行われているためと思われる。なんにしても、どのセクションを何番目に実行させるかは、制御不能。


とっちゃんに質問。PPL では、WriteConsole 関数ではなく、_tprintf 関数が使えると書かれていますね。しかし、実は OpenMP でも、_tprintf 関数は使えます。「スレッドセーフ」という話であれば、POSIX が「全ての関数がスレッドセーフであること」を要求しているので、POSIX に準拠している VC++ は、標準入出力関数はスレッドセーフですから、使っても問題はありません。

そういえば、CRT はソースが付いているので、デバッガーでステップ インをすれば追いかけられるな。では、追いかけてみよう。

まず、cout。「[VC をインストールしたディレクトリ]\ctr\src\ostream」の737行目辺りに入ってくる。ここの、748行目あたりの const typename _Myos::sentry _Ok(Ostr); でステップ イン。90行目あたりの、sentry クラスのコンストラクターに入ってくる。ベース クラスのコンストラクターにステップ イン。70行目あたりの、_Sentry_base クラスのコンストラクターに来る。ここに、「lock the stream buffer, if there」というコメントがある。「_Myostr.rbuf()->_Lock();」にステップ イン×2をすると、ファイルが「streambuf」に変わって、171行目辺りにある _Lock メソッドに入る。このメソッドをさらにステップ インすると、ファイルが「xmutex.cpp」に変わって、ミューテックスによるロックを使っていることがわかる。

次、wprintf(UNICODE が define されているので)。同じようにステップ インをしていくと、ファイル「_file.c」の260行目あたりから定義されている、_lock_file2 関数から EnterCriticalSection 関数を呼び出していることがわかる。

この様に VC++ の CRT は、ロックによってスレッドセーフにしていることがわかる。それなのに、なぜ元にしたコード(このページ)では、WriteConsole 関数を使っているのか、というのが問題です。なお、WriteConsole 関数に潜り込むことは出来ませんでした。threadPrint 関数を次のように修正して、実行してください。


void threadPrint(int num)
{
    #pragma omp parallel num_threads(4)
    {
        _tprintf(TEXT("セクション0[%d]\n"), omp_get_thread_num());
        #pragma omp sections
        {
            #pragma omp section
            _tprintf(TEXT("セクション1[%d]\n"), omp_get_thread_num());
            #pragma omp section
            _tprintf(TEXT("セクション2[%d]\n"), omp_get_thread_num());
            #pragma omp section
            _tprintf(TEXT("セクション3[%d]\n"), omp_get_thread_num());
            #pragma omp section
            _tprintf(TEXT("セクション4[%d]\n"), omp_get_thread_num());
        }
    }
}

「セクション0」は、全てのスレッドで実行されます。次の1~4のセクションを実行するスレッドが問題です。OpenMP では、全てのセクションを単一のスレッドで実行する場合が多くなります。そして、WriteConsole 関数を使うと、各セクションが別々のスレッドで実行される場合が多くなります。PPL では、どうでしょうか。

あっと、WriteConsole 関数のリファレンスに「WriteFile 関数に似た動作をします」と書いてある WriteFile 関数のリファレンスには、「この関数は、同期と非同期両方の操作を想定して設計されています。」と、書かれています。出力をバッファリングして、対象のストリームへ非同期に処理するようです。ということは、「スレッド セーフな関数を使って表示する。 Print the parameter values using thread-safe functions.」というコメントが、ヘン?この「using thread-safe functions.」というのは、「非同期処理」の意かと思われます。

投稿日時 : 2010年10月24日 14:14
コメント
  • # WriteFile における同期と非同期とは...
    とっちゃん's Blog
    Posted @ 2010/10/26 17:49
    WriteFile における同期と非同期とは...
  • # re: [OpenMP]分身!
    なぜ?
    Posted @ 2010/10/27 15:16
    さすがに一言言いたくなってきた。
    もうちょっと論理的に納得いく解釈をしてほしい。
    後何が言いたいのかもう少し分かりやすい文を書いてほしい。

    あなたの文章は独り言みたいのが多くてとても読みにくいです。
  • # re: [OpenMP]分身!
    Jitta
    Posted @ 2010/10/29 21:47
    コメントありがとうございます。

    申し訳ございませんが、このシリーズは、書き直しのための独り言です。
  • # ブランド激安市場コピー
    q3vm2up985
    Posted @ 2016/01/26 6:51
    http://www.ooobag.com/watch/rolex/gmt/36661e9d1fbe59d2.html
    日本ロレックス時計コピー品ロレックスレプリカ、日本ロレックス時計のロレックスコピー品ロレックス時計,ロレックスレプリカ,ロレックスコピー,日本ロレックス,ROLEX,ロレックスオーバーホール,ロレックスレプリカ ,ロレックス修理,ロレックスミルガウス,ロレックス中古,ロレックスサブマリーナ,ロレックスデイトナ,ロレックス中古,ロレックスアンティーク, }}}}}}
  • # シャネル財布激安
    lnxypnvlt@ocn.ne.jp
    Posted @ 2017/10/07 1:47
    コピー時計通販専門店
    人気コピー時計通販専門店

    ◆在庫情報随時更新!
    ◆お客さんたちも大好評です:
    ◆新品種類がそろっています。
    ◆品質がよい、価格が低い、実物写真!
    ◆経営方針: 品質を重視、納期も厳守、信用第一!
    ◆超格安価格で、安心、迅速、確実、にお客様の手元にお届け致します。

    豊富な品揃えで最新作も随時入荷致しておりますのでごゆっくりとご覧ください。
    広大な客を歓迎してご光臨!
  • # 誕生日
    jvubfrokorq@live.jp
    Posted @ 2021/12/04 11:12
    2019年春夏新作商品は販売!
    ■最新の2019年のブランドの新しいコピーを販売しています。
    100%の品質保証を■!満足は保証! 100パーセントの顧客導入率!
    ■優れた品質と低価格、本物より!
    ■送料は無料です!買うために安心してください!
    私たちの製品が良好であることを絶対的に自信を持って。
    高品質低価格保証救助
    製品の数、良い品質、低価格、多様う!
    動作原理:品質、密着性も宅配、評判に焦点が最初に私たちのポリシーです。
タイトル
名前
Url
コメント