none
静的メソッドについて RRS feed

  • 質問

  • 初めて投稿させていただきます。

     

    C#を使いプログラミングをしており、戻り値を返すメソッドを含むクラスを作成しようとしています。

    とくにインスタンスを生成する必要がないので以下のような感じのコーディングにしようかと思っています。

     

    public static class SampleClass

    {

    public static int GetInt(int x)

    {

    int i;

    i = x + 1;

    return i;

    }

    }

     

    上記のようなstaticを使ったコーディングをする際、以下の疑問に突き当たりました。

     

    ①メソッド内で宣言したローカル変数はメソッド終了時に破棄されるみたいですが、

     それは静的メソッドでも同じなんでしょうか。

     

    ②静的変数は同じメモリ領域なため、同時に処理がおこなわれる(マルチスレッドとか?)と誤った処理を

     してしまうことがあると認識していますが、静的メソッドや静的クラスを使ったときは大丈夫なんでしょうか。

     静的メソッド内では、引数やローカル変数(構造体もOK?)しか使わず、外部変数などは使わないとします。

     

    ③メソッドの動作がよくわかっていないのですが、ローカル変数はメソッドの実行時にメモリ上に確保されるとして、

     メソッド自体の元ネタみたいなのはどこにあるんでしょうか。

     

    ①、②については自分では質問のとおりなんじゃないかと思っているんですが、

    インターネットでこれだという回答が見つからなかったので質問させていただきました。

    よろしくお願いします。
    2008年12月12日 11:34

回答

  • 外池と申します。

     

    ご質問の趣旨は、

    • スレッドごとに独立したデータの記憶領域があるのか? ならば、あるスレッドが、別のスレッドのデータを壊すことはないわけです。逆に、スレッド間でデータのやりとりはできないです。
    • 複数のスレッドに共通のデータ記憶領域があるのか? こちらだと、あるスレッドが別のスレッドのデータを壊すことがあります。逆に上手にデータの更新のタイミングをとれば、スレッド間でデータの共有ができます。

    で、実際のところ、C#の場合、両方あるのですが、

    • プログラムの中でこの2者はどのように区別してプログラムすれば良いのか? あるいは、既存のプログラムで、どのように区別を読み取れば良いのか?

    そんな感じですよね? これ、私もよく整理されたドキュメントが欲しいところですが、あまり見かけません。経験的に理解していることを列挙しておきます。

     

    まず、スレッドの話は置いておいて、

     

    データの記憶領域には2種類あります。スタック領域というものと、ヒープ領域というものです。スタック領域はローカル変数の記憶に用いられていて、メソッドの呼び出し、実行の開始、メソッドの実行の終了(戻り)に応じて、ガラガラとデータの配置と廃棄が行われています。ヒープ領域は主にプログラムの実行を通じて、ある程度の期間のデータの保存、あるいは、プログラム実行中ずっと必要となるデータの保存に使われます。

     

    で、ご質問のスレッドと関係づけると、

     

    スタック領域は、スレッドごとに用意されています。ヒープ領域は基本的にスレッドごとには用意されていなくて、複数のスレッドに共通しています。

     

    あるメソッドの中のローカル変数は、すべてスタック領域に確保されます。つまり、スレッドごとに別けて確保されます。したがって、そのローカル変数が値型であれば、スレッド間で干渉しあうことはないでしょう。しかし、ローカル変数といっても参照型もあり、参照型はすなわちヒープ領域のデータを参照していますので、間接的にヒープ領域のデータがスレッド間で干渉してしまうことがあります。(ここで注意して頂きたいのは、この段落の最初に「あるメソッド」と書きましたが、staticなメソッドでなくてもいいです。staticなクラスに属している必要もありません。) あるメソッドのローカル変数を引数にして、別のメソッドを呼び出す。そして値が戻ってきて、元のメソッドのローカル変数に格納される。このようなやりとりはスタック上でなされるので、スレッド間で干渉する心配はしなくて良いです。

     

    値型の変数でヒープ領域に置かれるものもあります。staticなメンバー変数がそうです。これは、スレッド間で干渉します。(以下、追記します。) 他にも、そもそも、クラスから生成されたオブジェクトのインスタンスはヒープ領域に置かれるわけですが、このオブジェクトのメンバー変数は全てヒープ領域にありますので、値型といえどもスレッド間での干渉の対象になります。(追記終わり)

     

    なお、お断りですが「干渉」=「よくないこと」とは短絡的にお考えにならないように。「干渉」=「相互作用できる」ぐらいにご理解ください。最初に書きましたが、スレッド間で共有できるデータを、ちゃんとタイミングを考えて更新したり読み出ししたりすることは有益なことです。あるスレッドがデータの更新をしている間、他のスレッドが更新のジャマをしないように、あるいは中途半端な更新の最中に誤ったデータを読み出さないように、強制的に待たせる安全な機構を備えていることを「スレッドセーフ」と言います。

     

     

     

     

    2008年12月12日 14:23
  •  ハッシュドビーフ さんからの引用
    ①メソッド内で宣言したローカル変数はメソッド終了時に破棄されるみたいですが、

     それは静的メソッドでも同じなんでしょうか。

    はい。

     

     ハッシュドビーフ さんからの引用
    ②静的変数は同じメモリ領域なため、同時に処理がおこなわれる(マルチスレッドとか?)と誤った処理をしてしまうことがあると認識していますが、静的メソッドや静的クラスを使ったときは大丈夫なんでしょうか。静的メソッド内では、引数やローカル変数(構造体もOK?)しか使わず、外部変数などは使わないとします。

    静的かどうかとスレッドセーフかどうかは関係ありません。

    スレッドセーフかどうかは処理しだいです。引数に参照型が含まれるならスレッドセーフを保証できない可能性もありますし、生成したローカル変数がスレッドセーフを崩す処理を行うかもしれません。

     

     ハッシュドビーフ さんからの引用
    ③メソッドの動作がよくわかっていないのですが、ローカル変数はメソッドの実行時にメモリ上に確保されるとして、メソッド自体の元ネタみたいなのはどこにあるんでしょうか。

    元ネタって何でしょうか? 質問の意味が理解できませんでした。

    2008年12月12日 12:49
  •  ハッシュドビーフ さんからの引用

    初めて投稿させていただきます。

     

    ②静的変数は同じメモリ領域なため、同時に処理がおこなわれる(マルチスレッドとか?)と誤った処理を

     してしまうことがあると認識していますが、静的メソッドや静的クラスを使ったときは大丈夫なんでしょうか。

     静的メソッド内では、引数やローカル変数(構造体もOK?)しか使わず、外部変数などは使わないとします。

     

    既に外池さんから出ていますが、引数やローカル変数のみを使用し、外部的な変数を使用しないならばマルチスレッドでも大丈夫と思います。
    引数やローカル変数はスレッドごとにスタックに乗せられます。
    それらはマルチスレッドで干渉しません。
    ただ引数やローカル変数がオブジェクトを参照していて、それがスレッド間で共有される場合は別です。
    外部変数は使わないという言葉に、その意味が含まれていると思いました。
    これはstaticかに関わらず言えることです。

    2008年12月13日 2:11
  • (2) の件ですが、MDSN ライブラリとか .NET 2.0 SDK などは調べられたでしょう
    か?

     

    マネージ スレッド処理の実施
    http://msdn.microsoft.com/ja-jp/library/1c9txz50.aspx

     

    ここの「競合状態」を読むと参考になるかもしれません。もっと具体的な例もあり
    ます。例えば下記。

     

    スレッドセーフ コンポーネント
    http://msdn.microsoft.com/ja-jp/library/a8544e2s.aspx


     

    ところで、スレッドとは何かですが、

     

    マネージ スレッド処理の基本
    http://msdn.microsoft.com/ja-jp/library/hyz69czz.aspx

     

    の中の、

     

    スレッドおよびスレッド処理
    http://msdn.microsoft.com/ja-jp/library/6kac2kdh.aspx

     

    "オペレーティング システムは、実行中の各アプリケーションをプロセスを使用して
    分離します。スレッドとは、オペレーティング システムがプロセッサ時間を割り当
    てる対象の基本単位です。複数のスレッドが、そのプロセス内でコードを実行できま
    す。"

     

     

    (3) の件ですが、答えはメモリ上です。CPU は命令(メソッド自体の元ネタ)もデー
    タもメモリから読んで実行します。これはかなりプリミティブな話なので、MSDN ラ

    イブラリには出てないかもしれません。

     

    2008年12月13日 2:34

すべての返信

  •  ハッシュドビーフ さんからの引用
    ①メソッド内で宣言したローカル変数はメソッド終了時に破棄されるみたいですが、

     それは静的メソッドでも同じなんでしょうか。

    はい。

     

     ハッシュドビーフ さんからの引用
    ②静的変数は同じメモリ領域なため、同時に処理がおこなわれる(マルチスレッドとか?)と誤った処理をしてしまうことがあると認識していますが、静的メソッドや静的クラスを使ったときは大丈夫なんでしょうか。静的メソッド内では、引数やローカル変数(構造体もOK?)しか使わず、外部変数などは使わないとします。

    静的かどうかとスレッドセーフかどうかは関係ありません。

    スレッドセーフかどうかは処理しだいです。引数に参照型が含まれるならスレッドセーフを保証できない可能性もありますし、生成したローカル変数がスレッドセーフを崩す処理を行うかもしれません。

     

     ハッシュドビーフ さんからの引用
    ③メソッドの動作がよくわかっていないのですが、ローカル変数はメソッドの実行時にメモリ上に確保されるとして、メソッド自体の元ネタみたいなのはどこにあるんでしょうか。

    元ネタって何でしょうか? 質問の意味が理解できませんでした。

    2008年12月12日 12:49
  • > 元ネタって何でしょうか? 質問の意味が理解できませんでした。

     

    プログラム(コード)のことではないかと思いますが・・・

    2008年12月12日 13:00
  • 早速の回答ありがとうございます。

     

     Hongliang さんからの引用

    静的かどうかとスレッドセーフかどうかは関係ありません。

    スレッドセーフかどうかは処理しだいです。引数に参照型が含まれるならスレッドセーフを保証できない可能性もありますし、生成したローカル変数がスレッドセーフを崩す処理を行うかもしれません。

     

    静的かどうかは関係ないんですね。了解です。

    回答をいただいてまた疑問が浮かんでしまったんですが、

     

     >生成したローカル変数がスレッドセーフを崩す処理を行うかもしれません。

     

    とのことですが、引数にも値型しか指定しなかった場合、どのようなときにスレッドセーフが崩れるのでしょうか。

    参考URLなどでも構いませんが、よろしければご教授ください。

     

     

     Hongliang さんからの引用

    元ネタって何でしょうか? 質問の意味が理解できませんでした。

     

    すいません。自分でもどう言えばいいのかうまく表現できないのですが、

    メソッドの処理手順もどこかに記憶しておかないとうまく処理できないと思うのですが、

    その処理手順自体をメモリとかに保持しないのかなと思いまして。

    うまく伝わらないかもしれません…。

    なんというか、もともとのプログラムがなぜ動くのかというところがわかっていないのかもしれません。

    2008年12月12日 13:15
  • 外池と申します。

     

    ご質問の趣旨は、

    • スレッドごとに独立したデータの記憶領域があるのか? ならば、あるスレッドが、別のスレッドのデータを壊すことはないわけです。逆に、スレッド間でデータのやりとりはできないです。
    • 複数のスレッドに共通のデータ記憶領域があるのか? こちらだと、あるスレッドが別のスレッドのデータを壊すことがあります。逆に上手にデータの更新のタイミングをとれば、スレッド間でデータの共有ができます。

    で、実際のところ、C#の場合、両方あるのですが、

    • プログラムの中でこの2者はどのように区別してプログラムすれば良いのか? あるいは、既存のプログラムで、どのように区別を読み取れば良いのか?

    そんな感じですよね? これ、私もよく整理されたドキュメントが欲しいところですが、あまり見かけません。経験的に理解していることを列挙しておきます。

     

    まず、スレッドの話は置いておいて、

     

    データの記憶領域には2種類あります。スタック領域というものと、ヒープ領域というものです。スタック領域はローカル変数の記憶に用いられていて、メソッドの呼び出し、実行の開始、メソッドの実行の終了(戻り)に応じて、ガラガラとデータの配置と廃棄が行われています。ヒープ領域は主にプログラムの実行を通じて、ある程度の期間のデータの保存、あるいは、プログラム実行中ずっと必要となるデータの保存に使われます。

     

    で、ご質問のスレッドと関係づけると、

     

    スタック領域は、スレッドごとに用意されています。ヒープ領域は基本的にスレッドごとには用意されていなくて、複数のスレッドに共通しています。

     

    あるメソッドの中のローカル変数は、すべてスタック領域に確保されます。つまり、スレッドごとに別けて確保されます。したがって、そのローカル変数が値型であれば、スレッド間で干渉しあうことはないでしょう。しかし、ローカル変数といっても参照型もあり、参照型はすなわちヒープ領域のデータを参照していますので、間接的にヒープ領域のデータがスレッド間で干渉してしまうことがあります。(ここで注意して頂きたいのは、この段落の最初に「あるメソッド」と書きましたが、staticなメソッドでなくてもいいです。staticなクラスに属している必要もありません。) あるメソッドのローカル変数を引数にして、別のメソッドを呼び出す。そして値が戻ってきて、元のメソッドのローカル変数に格納される。このようなやりとりはスタック上でなされるので、スレッド間で干渉する心配はしなくて良いです。

     

    値型の変数でヒープ領域に置かれるものもあります。staticなメンバー変数がそうです。これは、スレッド間で干渉します。(以下、追記します。) 他にも、そもそも、クラスから生成されたオブジェクトのインスタンスはヒープ領域に置かれるわけですが、このオブジェクトのメンバー変数は全てヒープ領域にありますので、値型といえどもスレッド間での干渉の対象になります。(追記終わり)

     

    なお、お断りですが「干渉」=「よくないこと」とは短絡的にお考えにならないように。「干渉」=「相互作用できる」ぐらいにご理解ください。最初に書きましたが、スレッド間で共有できるデータを、ちゃんとタイミングを考えて更新したり読み出ししたりすることは有益なことです。あるスレッドがデータの更新をしている間、他のスレッドが更新のジャマをしないように、あるいは中途半端な更新の最中に誤ったデータを読み出さないように、強制的に待たせる安全な機構を備えていることを「スレッドセーフ」と言います。

     

     

     

     

    2008年12月12日 14:23
  •  ハッシュドビーフ さんからの引用

    初めて投稿させていただきます。

     

    ②静的変数は同じメモリ領域なため、同時に処理がおこなわれる(マルチスレッドとか?)と誤った処理を

     してしまうことがあると認識していますが、静的メソッドや静的クラスを使ったときは大丈夫なんでしょうか。

     静的メソッド内では、引数やローカル変数(構造体もOK?)しか使わず、外部変数などは使わないとします。

     

    既に外池さんから出ていますが、引数やローカル変数のみを使用し、外部的な変数を使用しないならばマルチスレッドでも大丈夫と思います。
    引数やローカル変数はスレッドごとにスタックに乗せられます。
    それらはマルチスレッドで干渉しません。
    ただ引数やローカル変数がオブジェクトを参照していて、それがスレッド間で共有される場合は別です。
    外部変数は使わないという言葉に、その意味が含まれていると思いました。
    これはstaticかに関わらず言えることです。

    2008年12月13日 2:11
  • (2) の件ですが、MDSN ライブラリとか .NET 2.0 SDK などは調べられたでしょう
    か?

     

    マネージ スレッド処理の実施
    http://msdn.microsoft.com/ja-jp/library/1c9txz50.aspx

     

    ここの「競合状態」を読むと参考になるかもしれません。もっと具体的な例もあり
    ます。例えば下記。

     

    スレッドセーフ コンポーネント
    http://msdn.microsoft.com/ja-jp/library/a8544e2s.aspx


     

    ところで、スレッドとは何かですが、

     

    マネージ スレッド処理の基本
    http://msdn.microsoft.com/ja-jp/library/hyz69czz.aspx

     

    の中の、

     

    スレッドおよびスレッド処理
    http://msdn.microsoft.com/ja-jp/library/6kac2kdh.aspx

     

    "オペレーティング システムは、実行中の各アプリケーションをプロセスを使用して
    分離します。スレッドとは、オペレーティング システムがプロセッサ時間を割り当
    てる対象の基本単位です。複数のスレッドが、そのプロセス内でコードを実行できま
    す。"

     

     

    (3) の件ですが、答えはメモリ上です。CPU は命令(メソッド自体の元ネタ)もデー
    タもメモリから読んで実行します。これはかなりプリミティブな話なので、MSDN ラ

    イブラリには出てないかもしれません。

     

    2008年12月13日 2:34
  • 外池さん バックスマッシュさん

     

    詳しく説明してくださったおかげで

    ある程度把握できたような気がします。

    ありがとうございました。

     

     

    SurferOnWwwさん

     

    MSDNライブラリは読んだりはしてみたのですが、

    私の解釈であっているのか不安に思うことところもあったり、

    そもそもの知識がなかったりで質問させていただいた次第です。

    参考URLならびに(3)の回答ありがとうございました。

    2008年12月14日 14:50