C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: 文字列を複数スレッドからアクセス(排他制御)


(過去ログ 171 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■98368 / inTopicNo.1)  文字列を複数スレッドからアクセス(排他制御)
  
□投稿者/ ロジ (1回)-(2021/11/07(Sun) 16:11:48)

分類:[C#] 

■開発環境
OS: Windows 10
NET Framework バージョン: 4.6.2
プログラミング言語: C#
開発ツール: Visual Studio Community 2017
補足: Visual Studio での C/C++ の開発経験は 20 年以上あります。


ある文字列を複数スレッドから読み書きする際の排他制御について教えて下さい。

ある文字列の使用は以下の様になります。
・メインスレッドで定期的に書き換える。
・サブスレッド(複数)で定期的に取得し、使用する。

尚、以下のフローは問題なしとします。
1. サブスレッド 1 が文字列を取得する。(文字列を保持するインスタンスのゲッター呼出し)
2. その直後にメインスレッドが文字列を書き換える。(文字列を保持するインスタンスのセッター呼出し)
3. サブスレッド 1 が取得した文字列を使用する。(この時点では書き換え前の文字列ですが、それは問題なしとする)
  サブスレッドは使用の前に必ず文字列を取得する。


文字列の排他制御は以下のようなクラス(AもしくはB)のインスタンスを生成し、実現しようと思っています。
どちらのクラスを使用するのが正しいのでしょうか。
どちらも正しくない場合、どのようにしたらよいか教えて下さい。


クラスA:
ゲッターは保持している文字列と同じ内容の別文字列を渡します。(保持しているインスタンスとは別のインスタンスを渡す)
セッターは入力と同内容の別文字列を保持します。(入力とは別のインスタンスを保持)

クラスB:
ゲッターは保持している文字列そのものを渡します。(保持しているインスタンスを渡す)
セッターは入力の文字列そのものを保持します。(入力の文字列を保持)


クラスBでも良いと思ったのですが、1文字など短い文字列だった場合、文字はキャッシュメモリに配置され、他のスレッドからアクセスした場合には最新の内容に更新されていないメモリをアクセスする事態もあると思ったのですが、如何でしょうか。

クラスAとBの違いは以下の様に思っています。
・クラスAは文字列を構成する「各文字」を排他制御している。
・クラスBは文字列を「構成する文字が格納されているメモリの位置(つまりメモリアドレス)」を排他制御している(文字は排他制御していない)。


以下にクラスAとクラスBを示します。

class A
{
  object _lockObj = new object();
  string _strValue;
  public string Value
  {
    get
    {
      string str;
      lock( _lockObj )
      {
        if( _strValue == null )
        {
          str = null;
        }
        else
        {
          str = string.Copy( _strValue );
        }
      }
      return str;
    }
    set
    {
      lock( _lockObj )
      {
        if( value == null )
        {
          _strValue = null;
        }
        else
        {
          _strValue = string.Copy( value );
        }
      }
    }
  }
}

class B
{
  object _lockObj = new object();
  string _strValue;
  public string Value
  {
    get
    {
      string str;
      lock( _lockObj )
      {
        str = _strValue;
      }
      return str;
    }
    set
    {
      lock( _lockObj )
      {
        _strValue = value;
      }
    }
  }
}

引用返信 編集キー/
■98369 / inTopicNo.2)  Re[1]: 文字列を複数スレッドからアクセス(排他制御)
□投稿者/ Azulean (1208回)-(2021/11/07(Sun) 19:02:43)
2021/11/07(Sun) 19:04:55 編集(投稿者)

.NET における string は生成時から不変です。
C/C++ 言語のような「同じメモリ上を書き換えている」というようなイメージとは異なります。

string はできた時点で「完成」しており、「不変(書き換え不可)」です。
書き換えたかのように見える時は、基本的に「新しいインスタンスを生成している」に過ぎません。

少なくとも、A のような配慮は不要です。
(たぶん、B の lock すら不要だと思うけど…)
引用返信 編集キー/
■98372 / inTopicNo.3)  Re[1]: 文字列を複数スレッドからアクセス(排他制御)
□投稿者/ 古谷 (13回)-(2021/11/08(Mon) 10:52:39)
No98368 (ロジ さん) に返信
> クラスBでも良いと思ったのですが、1文字など短い文字列だった場合、文字はキャッシュメモリに配置され、他のスレッドからアクセスした場合には最新の内容に更新されていないメモリをアクセスする事態もあると思ったのですが、如何でしょうか。

そういう事態は起こらないよ

なぜならば、

C# メモリ モデルの理論と実践
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2012/december/csharp-the-csharp-memory-model-in-theory-and-practice
> スレッドがロックを取得するときには必ず、CLR はそのスレッドが、以前にロックを保持していたスレッドが行ったすべての更新を読み取ることを保証します。

とあるので、可視性は保証されるよ
引用返信 編集キー/
■98373 / inTopicNo.4)  Re[2]: 文字列を複数スレッドからアクセス(排他制御)
□投稿者/ 古谷 (14回)-(2021/11/08(Mon) 11:22:28)
class Bで良いとは思うのだけれども

_strValueの可視性を気にしてlockするなら同じように_lockObjの可視性も気にしないといけないんじゃないかな

Javaだとfinal フィールドのセマンティクスがあってfinalのフィールドはコンストラクトが完了した時点で可視性を保証するっていうのがあるのだけれども、.NETのreadonlyにも同じような効果があるのかな、よくわからんな、よくわらんから_lockObjにはvolatileとか付けといたが良い気がする
引用返信 編集キー/
■98375 / inTopicNo.5)  Re[3]: 文字列を複数スレッドからアクセス(排他制御)
□投稿者/ ???W (1回)-(2021/11/09(Tue) 00:24:07)
Azulean さん、古谷さん、教えて頂き、ありがとうございます。

クラスBで大丈夫という見解を頂けてホッとしました。

可視性……。しらない用語だ。
別途調べてみます!

提示頂いたURLの内容はまだ見れていないのですが、一旦は解決とさせて頂きます。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -