東方算程譚

Oriental Code Talk ── επιστημηが与太をこく、弾幕とは無縁のシロモノ。

目次

Blog 利用状況

ニュース

著作とお薦めの品々は

著作とお薦めの品々は
東方熱帯林へ。

あわせて読みたい

わんくま

  1. 東京勉強会#2
    C++/CLI カクテル・レシピ
  2. 東京勉強会#3
    template vs. generics
  3. 大阪勉強会#6
    C++むかしばなし
  4. 東京勉強会#7
    C++むかしばなし
  5. 東京勉強会#8
    STL/CLRによるGeneric Programming
  6. TechEd 2007 @YOKOHAMA
    C++・C++/CLI・C# 適材適所
  7. 東京勉強会#14
    Making of BOF
  8. 東京勉強会#15
    状態遷移
  9. 名古屋勉強会#2
    WinUnit - お気楽お手軽UnitTest

CodeZine

  1. Cで実現する「ぷちオブジェクト指向」
  2. CUnitによるテスト駆動開発
  3. SQLiteで組み込みDB体験(2007年版)
  4. C++/CLIによるCライブラリの.NET化
  5. C# 1.1からC# 3.0まで~言語仕様の進化
  6. BoostでC++0xのライブラリ「TR1」を先取りしよう (1)
  7. BoostでC++0xのライブラリ「TR1」を先取りしよう (2)
  8. BoostでC++0xのライブラリ「TR1」を先取りしよう (3)
  9. BoostでC++0xのライブラリ「TR1」を先取りしよう (4)
  10. BoostでC++0xのライブラリ「TR1」を先取りしよう (5)
  11. C/C++に対応した、もうひとつのUnitTestFramework ─ WinUnit
  12. SQLiteで"おこづかいちょう"
  13. STL/CLRツアーガイド
  14. マージ・ソート : 巨大データのソート法
  15. ヒープソートのアルゴリズム
  16. C++0xの新機能「ラムダ式」を次期Visual Studioでいち早く試す
  17. .NETでマンデルブロ集合を描く
  18. .NETでマンデルブロ集合を描く(後日談)
  19. C++/CLI : とある文字列の相互変換(コンバージョン)
  20. インテルTBBによる選択ソートの高速化
  21. インテルTBB3.0 によるパイプライン処理
  22. Visual C++ 2010に追加されたSTLアルゴリズム
  23. Visual C++ 2010に追加されたSTLコンテナ「forward_list」
  24. shared_ptrによるObserverパターンの実装
  25. .NETでマンデルブロ集合を描く(番外編) ── OpenCLで超並列コンピューティング
  26. StateパターンでCSVを読む
  27. 状態遷移表からStateパターンを自動生成する
  28. 「ソートも、サーチも、あるんだよ」~標準C++ライブラリにみるアルゴリズムの面白さ
  29. インテルTBBの同期メカニズム
  30. なぜsetを使っちゃいけないの?
  31. WPFアプリケーションで腕試し ~C++でもWPFアプリを
  32. C++11 : スレッド・ライブラリひとめぐり
  33. Google製のC++ Unit Test Framework「Google Test」を使ってみる
  34. メールでデータベースを更新するココロミ
  35. Visitorパターンで遊んでみたよ
  36. Collection 2題:「WPFにバインドできる辞書」と「重複を許す検索set」
  37. Visual C++ 2012:stateless-lambdaとSQLiteのぷち拡張
  38. 「Visual C++ Compiler November 2012 CTP」で追加された6つの新機能

@IT

  1. Vista時代のVisual C++の流儀(前編)Vista到来。既存C/C++資産の.NET化を始めよう!
  2. Vista時代のVisual C++の流儀(中編)MFCから.NETへの実践的移行計画
  3. Vista時代のVisual C++の流儀(後編) STL/CLRによるDocument/Viewアーキテクチャ
  4. C++開発者のための単体テスト入門 第1回 C++開発者の皆さん。テスト、ちゃんとしていますか?
  5. C++開発者のための単体テスト入門 第2回 C++アプリケーションの効率的なテスト手法(CppUnit編)
  6. C++開発者のための単体テスト入門 第3回 C++アプリケーションの効率的なテスト手法(NUnit編)

AWARDS


Microsoft MVP
for Visual Developer - Visual C++


Wankuma MVP
for いぢわる C++


Nyantora MVP
for こくまろ中国茶

Xbox

Links

記事カテゴリ

書庫

日記カテゴリ

生産者 と 消費者

ネタ元 → 並列プログラミングの効率的なデバッグを実現する「Parallel Inspector」

# 記事の本筋とは離れるのでネタ元にコメントするのは差し控えるけども。
このサンプル、ひとつの変数に複数のスレッドからアクセスするときの
問題を示しているわけだが、いわゆる「生産者/消費者(producer/consumer)」の
イディオムにはなってねぇ!

生産者/消費者パターンちうのは:
- 生産者は製品を製造し棚に並べる。棚が一杯なら空くまで待つ。
- 消費者は製品を棚から取る。棚が空なら品出しを待つ。
生産者スレッドの生産能力 と 消費者スレッドの購買能力 には
バラつき/ムラがあり、そのバラつきを吸収するのが棚(在庫)なんだが、
棚の容量には限りがあるのでたまにどちらかが待つことになります。

MSDN漁ってみたらばまさにドンピシャなサンプルがありました。
方法 : producer スレッドと consumer スレッドを同期する (C# プログラミング ガイド)

ここんとこC++/CLIから離れてたのでリハビリがてらportしてみた。
※ C++/CLI には lock 構文を持ち合わせてないんで、MonitorのEnter/Exitで代用

using namespace System;
using namespace System::Threading;
using namespace System::Collections::Generic;

// 同期イベント: 「アイテムが追加された」と「終了しろ」
public ref class SyncEvents {
public:
  SyncEvents() {
    eventArray_    = gcnew array<EventWaitHandle^>(2);
    eventArray_[0] = gcnew AutoResetEvent(false);
    eventArray_[1] = gcnew ManualResetEvent(false);
  }

  property EventWaitHandle^ ExitThreadEvent // 「終了しろ」
    { EventWaitHandle^ get() { return eventArray_[1]; } }
  property EventWaitHandle^ NewItemEvent // 「アイテムが追加された」
    { EventWaitHandle^ get() { return eventArray_[0]; } }
  property array<EventWaitHandle^>^ EventArray // ふたつまとめて
    { array<EventWaitHandle^>^ get() { return eventArray_; } }

private:
  // eventArray_[0]:「アイテムが追加された」
  // eventArray_[1]:「終了しろ」
  array<EventWaitHandle^>^ eventArray_;
};

/*
 * 生産者
 */
public ref class Producer  {
public:
    Producer(Queue<int>^ q, SyncEvents^ e) : queue_(q), syncEvents_(e) {}

  // Producer.ThreadRun
  void ThreadRun() {
    int count = 0;
    Random r;
    // 「終了しろ」でない間、Queueにアイテムを追加する
    while (!syncEvents_->ExitThreadEvent->WaitOne(0, false)) {
      Object^ sync = ((System::Collections::ICollection^)queue_)->SyncRoot;
      Monitor::Enter(sync);
      try {
        while ( queue_->Count < 20 ) { // 倉庫に余裕がある間
          queue_->Enqueue(r.Next(0,100)); // アイテムを追加して
          syncEvents_->NewItemEvent->Set(); // 「アイテムが追加された」イベント発行

          count++;
        }
      } finally {
        Monitor::Exit(sync);
      }
    }
    Console::WriteLine(L"Producer thread: produced {0} items", count);
  }

private:
  Queue<int>^ queue_;
  SyncEvents^ syncEvents_;
};

/*
 * 消費者
 */
public ref class Consumer {
public:
    Consumer(Queue<int>^ q, SyncEvents^ e) : queue_(q), syncEvents_(e) {}

  // Consumer.ThreadRun
  void ThreadRun() {
    int count = 0;
    // イベント:「終了しろ」「アイテムが追加された」のいずれかを待ち、
    // そのイベントが「終了しろ」でない間

    while (WaitHandle::WaitAny(syncEvents_->EventArray) != 1) {
      Object^ sync = ((System::Collections::ICollection^)queue_)->SyncRoot;
      Monitor::Enter(sync);
      try {
        int item = queue_->Dequeue(); // アイテムを取り出す
      } finally {
        Monitor::Exit(sync);
      }
      count++;
    }
    Console::WriteLine(L"Consumer Thread: consumed {0} items", count);
  }

private:
  Queue<int>^ queue_;
  SyncEvents^ syncEvents_;
};

/*
 * 生産者/消費者を起こすメイン・スレッド
 */
public ref class ThreadSyncSample {
private:
  // 在庫(Queue)の内容をダンプ
  static void ShowQueueContents(Queue<int>^ q) {
    Object^ sync = ((System::Collections::ICollection^)q)->SyncRoot;
    Monitor::Enter(sync);
    try {
      for each (int item in q ) {
        Console::Write(L"{0} ", item);
      }
    } finally {
      Monitor::Exit(sync);
    }
    Console::WriteLine();
  }

public:
  static void Main() {
    Queue<int> queue = gcnew Queue<int>();
    SyncEvents syncEvents;

    // 生産者/消費者を生成し、それぞれのエントリでスレッドを用意
    Console::WriteLine(L"Configuring worker threads...");
    Producer producer(%queue, %syncEvents);
    Consumer consumer(%queue, %syncEvents);
    Thread producerThread(gcnew ThreadStart(%producer, &Producer::ThreadRun));
    Thread consumerThread(gcnew ThreadStart(%consumer, &Consumer::ThreadRun));

    // 両スレッドを開始
    Console::WriteLine(L"Launching producer and consumer threads...");
    producerThread.Start();
    consumerThread.Start();

    // たまにQueueをダンプ
    for (int i=0; i<4; i++ ) {
      Thread::Sleep(2500);
      ShowQueueContents(%queue);
    }

    // 「終了しろ」イベントを起こす
    Console::WriteLine(L"Signaling threads to terminate...");
    syncEvents.ExitThreadEvent->Set();

    // 両スレッドの終了を待つ
    producerThread.Join();
    consumerThread.Join();
  }

};

int main() {
  ThreadSyncSample::Main();
}

投稿日時 : 2009年9月16日 10:05

コメントを追加

# re: 生産者 と 消費者 2009/09/16 10:46 επιστημη

おっとチョンボ。
static void Main() { の直後:
× Queue<int> queue = gcnew Queue<int>();
○ Queue<int> queue;

# re: 生産者 と 消費者 2009/09/16 17:57 Jitta

じゃ、別方面から攻めてみる(ちげー

# re: 生産者 と 消費者 2009/09/16 18:05 Jitta

ボソボソ
無理矢理作るから、おかしなコードになるんだよなぁ。
前回に「パフォーマンスが上がらないと、お客様が怒ることになる」と言ったコードをサンプルとして出すって、どうよ?

# re: 生産者 と 消費者 2009/09/16 18:05 Jitta

ボソボソ
無理矢理作るから、おかしなコードになるんだよなぁ。
前回に「パフォーマンスが上がらないと、お客様が怒ることになる」と言ったコードをサンプルとして出すって、どうよ?

# re: 生産者 と 消費者 2009/09/16 18:08 Jitta

うが~!
何回送ってもエラーになるし、戻って再読み込みしても送ったのが表示されなかったのに~(涙)

# VBでProducer/Consumerパターン 2009/09/16 22:31 まさるblog

VBでProducer/Consumerパターン

# re: 生産者 と 消費者 2009/09/16 23:18 Soda

オリジナルのコメントで「商品を生産してから商品を販売します。」と書かれている時点で、並列になってないんですが(^^;
まぁ、オリジナルは「生産者/消費者パターン」と言ってるわけじゃないから、その点は可哀想かなと(^^;

オリジナルは、倉庫が1つで在庫数が変化しているから、ちと意味合いが違うかなーとか思ったり。
あっ、倉庫の中に棚が有限で存在し、1つの棚には1つの商品しか置かないなら同じになるのか・・・
棚を数値にしても、ちゃんと独立して行動できれば、「生産者/消費者パターン」ってことでいいのかな?

同期付けたらスレッドの意味がなくなってるってのは、駄目な例だと思いますがw
ありがちなバグじゃなくて、設計ミス・・・だよなぁ(^^;
さらに言うと、あのサンプルでは、スレッドにおける読み込みと書き込みの同時発生率アップにはなってないですよね?
在庫数をバックアップしてしまうからズレるだけで・・・
在庫数がおかしくなることを優先させた結果、デバッガが検出している内容とは無関係なバグを仕込んでるような気がする(^^;
結果的にSleep抜いても検出できている可能性が高い・・・はずだよね(^^;

# psHXcNWfIRMXHt 2014/08/28 14:40 http://crorkz.com/

5iOOAg Hi, i think that i saw you visited my site thus i came to ???return the favor???.I'm attempting to find things to improve my site!I suppose its ok to use some of your ideas!!

# dCdRDsIcCP 2014/09/02 20:29 https://www.youtube.com/watch?v=KLiKPsOc22A

Hi, i think that i noticed you visited my blog so i got here to ???return the favor???.I am trying to in finding things to improve my site!I suppose its ok to make use of a few of your concepts!!

# DlhtgWVXiGZpyNS 2014/09/11 20:47 http://www.ukessays.com

you could have a terrific blog here! would you prefer to make some invite posts on my blog?

# kKmPQDhmEDsFrPm 2014/09/14 20:12 http://distancity.com/

Well I truly liked studying it. This tip offered by you is very practical for good planning.

# I always spent my half an hour to read this blog's content all the time along with a cup of coffee. 2018/09/05 5:40 I always spent my half an hour to read this blog's

I always spent my half an hour to read this blog's content
all the time along with a cup of coffee.

# Wonderful blog! I found it while browsing on Yahoo News. Do you have any suggestions on how to get listed in Yahoo News? I've been trying for a while but I never seem to get there! Thanks 2018/09/05 5:41 Wonderful blog! I found it while browsing on Yahoo

Wonderful blog! I found it while browsing on Yahoo News.

Do you have any suggestions on how to get listed in Yahoo News?

I've been trying for a while but I never seem to get there!
Thanks

# I will immediately seize your rss feed as I can not in finding your e-mail subscription hyperlink or newsletter service. Do you have any? Please permit me recognize so that I could subscribe. Thanks. 2018/10/26 2:16 I will immediately seize your rss feed as I can no

I will immediately seize your rss feed as I can not in finding your e-mail subscription hyperlink
or newsletter service. Do you have any? Please permit me recognize so that I
could subscribe. Thanks.

タイトル
名前
URL
コメント