Thread間のデータ共有の仕組みについて


投稿ツリー



前の投稿 - 次の投稿 | 親投稿 - 子投稿.1 | 投稿日時 2013/10/3 0:00
ゲスト 
質問するフォーラム間違えてしまい承認されなかったようなのでこちらで....汗
下記のコードのWriter._cntの挙動についての質問です。
import core.thread;
import std.stdio;
import std.concurrency;

class Writer {
  public:
    this() {
      _tg = new ThreadGroup;
      _cnt = 0;
    }

    void write_tg(string text, int timeout) {
      _tg.create({
        while (1) {
          ++_cnt;
          writeln(_cnt);
          writeln(text);
          writeln();
          Thread.sleep(dur!"seconds"(timeout));
        }
      });
    }

    void write_spawn(string text, int timeout) {
      spawn(&write, text, timeout, _cnt);
    }

  private:
    ThreadGroup _tg;
    int _cnt;
}

void write(string text, int timeout, ref int cnt) {
  while (1) {
    ++cnt;
    writeln(cnt);
    writeln(text);
    writeln();
    Thread.sleep(dur!"seconds"(timeout));
  }
}

void main() {
  Writer writer = new Writer;

  /*
  writer.write_spawn("3秒毎spawn", 3);
  writer.write_spawn("1秒毎spawn", 1);
  writer.write_spawn("10秒毎spawn", 10);
  */
  /*
  writer.write_tg("3秒毎tg", 3);
  writer.write_tg("1秒毎tg", 1);
  writer.write_tg("10秒毎tg", 10);
  */
}
上記のコードでWriter.write_tgを呼んだ場合、Writer.write_spawnを呼んだ場合と違ってWriter._cntがThread間でデータ共有されているようなのですが、これはどういった仕組みなのでしょうか?
データ共有するためにはsharedをつけるか、immutableなデータとして扱うか、の2通りしかないと思っていたのですが、この場合のWriter._cntはどのような立場にあるのでしょうか?

よろしくお願いします。
投票数:0 平均点:0.00
返信する
前の投稿 - 次の投稿 | 親投稿 - 子投稿.1 | 投稿日時 2013/10/16 1:39
SHOO  管理人   投稿数: 658
御返事遅くなりまして申し訳ありません。
ご回答いたします。

>上記のコードでWriter.write_tgを呼んだ場合、Writer.write_spawnを呼んだ場合と違ってWriter._cntがThread間でデータ共有されているようなのですが、これはどういった仕組みなのでしょうか?

D言語では、グローバル変数やstatic変数はデフォルトでスレッドローカルストレージに配置され、sharedストレージクラスにしないと複数のスレッドから操作することができません。
言い換えると、グローバル変数やstatic変数以外はスレッド間で共有することができるのです。

spawnでは、基本的にグローバル領域か引数を通してしか関数外の変数にアクセス出来ないため、引数の型をチェック(hasUnsharedAliasingな引数がないことを確認)することでスレッドを超えたデータ共有を制限しています。

また、spawnで共有データを参照していない(スレッドローカルストレージのデータを参照している)ように見えるのは、spawnのシグニチャが

Tid spawn(F, T...)( F fn, T args )

だからですね。(T args、となっていて、ref引数ではない)
すなわち、spawnを呼び出した時点で、_cntのコピーが作成され、そのコピーをwriteのrefによって参照してカウントアップを行っている(元のデータは更新されない)、ということです。


> データ共有するためにはsharedをつけるか、immutableなデータとして扱うか、の2通りしかない

その認識であっていますよ。ただし、sharedとデータがスレッドローカルストレージに配置されるかどうかの関係は、グローバル変数とstatic変数で使われる場合だけ関係します。

参考: http://qiita.com/mono_shoo/items/c759e64ebc1c8507b483
投票数:1 平均点:10.00
返信する
前の投稿 - 次の投稿 | 親投稿 - 子投稿.1 | 投稿日時 2013/10/16 21:22
ゲスト 
丁寧な解説ありがとうございます。

引用:
グローバル変数やstatic変数以外はスレッド間で共有することができる

なるほど...目から鱗です。

1つ質問なのですが、上記以外の普通の変数は、デフォルトでTLSを使用しているところを暗黙の内にsharedがつくことによって共有可能にしているのでしょうか?それとも、最初からグローバルなデータセグメントのようなものを使用しているのでしょうか?

再度よろしくお願いします。
投票数:0 平均点:0.00
返信する
前の投稿 - 次の投稿 | 親投稿 - 子投稿.1 | 投稿日時 2013/10/16 22:34
SHOO  管理人   投稿数: 658
>普通の変数は、デフォルトでTLSを使用しているところを暗黙の内にsharedがつくことによって共有可能にしているのでしょうか?それとも、最初からグローバルなデータセグメントのようなものを使用しているのでしょうか?

(グローバル変数やstatic変数でない)普通の変数は、スタック領域やヒープ領域に確保されますが、これらはGCに管理されているか否かにかかわらず、すべてのスレッドから参照することができます。
ので、

>最初からグローバルなデータセグメントのようなものを使用している

のほうが認識としては近いと思います。

sharedをつけるというのは、(TLSに配置されなくなることを明示するための)ストレージクラスとしてのsharedを除いて、今のところはただ型が変わるだけです。
TDPLの13.12にはアトミック操作云々メモリバリア云々の話が書いてあるけどそんな気の利いたことは現時点ではしていません。…よね?(あまりにも自信満々に書いてあるので不安になる

「shared型にすることで複数スレッド間でデータが共有され、shared型の変数を取り扱うには、ちゃんとアトミック操作やら同期制御やらを使う必要があり、そうすることではじめて複数のスレッドから操作することができるようになります。」
よりも、
「データを複数スレッド間で共有するときはshared型でマークすると判りやすくなり、shared型の変数を取り扱うときは、ちゃんとアトミック操作やら同期制御やらを使うことで、複数のスレッドから安全に操作することができます。」
のほうが正解に近いイメージですね。
投票数:1 平均点:10.00
返信する
前の投稿 - 次の投稿 | 親投稿 - 子投稿なし | 投稿日時 2013/10/16 23:45
ゲスト 
早速のご返信ありがとうございます。

お陰様で疑問点が解消できました。

二度にわたる丁寧な解説、本当にありがとうございました。
投票数:0 平均点:0.00
返信する

このトピックに投稿する

題名
ゲスト名
投稿本文

  条件検索へ


メインメニュー

ログイン

ユーザー名:


パスワード:





パスワード紛失  |新規登録

Menu