0

アイデアは、から値(「データ」)を取得することです

  • 変数
  • 存在しない場合は、ファイルから
  • 存在しない場合は、サーバーから
public static string LoadData(int id)
{
    if (_isDataLoaded[id - 1])
    {
      return _data[id - 1];
    }

    if (File.Exists(_fileName))
    {
        bool dataExists;
        string cachedData;
        if (GetCachedConstant(id, out cachedData)) //read from a file
        {
            lock (_locker)
            {
                _isDataLoaded[id - 1] = true;
                _data[id - 1] = cachedData;
            }

            return _data[id - 1];
        }
    }

    string remoteData = GetFromServer(id);
    lock (_locker)
    {
        CacheToFile(id, remoteData); //write to a file
        _data[id - 1] = remoteData;
        _isDataLoaded[id - 1] = true;
    }

    return _data[id - 1];
}

このコードは多くのスレッドで使用されています。スレッドセーフのように見えますが、実際にはそうではありません。私はそれをテストしたので、それは私にその考えを与え、私を確信させます。_data書き込む前に、既存のものを再確認する必要があります。たとえば、通常はパターンシングルトンで使用されるダブルチェックのようにする必要があります。

誰かが私にそれをここで実装する方法を理解させてください?

編集:

string[] _data;
bool[] _isDataLoaded.

EDIT2

上記のコードは.NET<4.0で使用される可能性があるため、そこでLasyを使用することは許可されていません。私が今持っている唯一の質問は、ダブルチェックロックを使用する場合に備えて、揮発性を使用する必要があるかどうかです。

volatile string[] _data;
volatile bool[] _isDataLoaded.
4

6 に答える 6

3

スレッドの問題の2つの明らかな原因を見ることができます

  • タイプとは何_dataですか_isDataLoadedこれらのタイプがスレッドセーフでない 場合、2つのスレッドが同じ要素にアクセスしない場合にのみ、コード配列もスレッドセーフになりません。これは、上記のコードで発生する可能性があります。代わりにConcurrentDictionaryを使用してみてください。
  • GetCachedConstantの正確な実装に応じて、スレッドAがキャッシュされたデータのファイルへの書き込みを開始し、実際にはファイルに部分的に書き込まれたデータしか含まれていない場合にCacheToFileスレッドBがルートをたどる可能性がある競合状態が発生する可能性があります。File.Exists

犯人はこれら2つのうちの最初のものだと思います。他の問題があるかもしれませんが、これらのタイプがスレッドセーフでない限り、これらのオブジェクトへのアクセスも同期しない限り、ダブルチェックロックの量を節約することはできません。

于 2012-11-22T16:05:49.163 に答える
2

ファイルとネットワークからのフェッチが渡されたデリゲートで行われる、に_data置き換えます。Lazy<string>[]

したがって、静的コンストラクターで次のようなことを行います。

_data=new Lazy<string>[maxId+1];
for(int i=0;i<_data.Length;i++)
{
  _data[i]=new Lazy<string>(()=>fetchData(i), LazyThreadSafetyMode.ExecutionAndPublication);
}

次に、値を単純に取得します_data[i].Value


本当にダブルチェックロックが必要な場合は、基本的に次のように機能します。

if (!_isDataLoaded[id - 1])
{
    lock(locker)
    {
        if(!_isDataLoaded[id - 1])
        {
            ...
            _data[id - 1] = ...;
            _isDataLoaded[id - 1] = true;
        }
    }
}
return _data[id - 1];

このコードの問題は、実際に機能することが保証されているかどうかを判断するのが難しいことです。これは、実行しているプラ​​ットフォームのメモリモデルによって異なります。AFAIK .net 2.0メモリモデルはこれが機能することを保証しますが、ECMACLRモデルとJavaモデルは機能しません。メモリモデルの問題は非常に微妙で、間違えやすいです。したがって、このパターンは使用しないことを強くお勧めします。

于 2012-11-22T16:12:38.943 に答える
1

メソッドの最初でロックしてみませんか。これにより、データ(キャッシュ)が常に有効で一貫性のある状態になります。

于 2012-11-22T16:02:17.797 に答える
1

これはどうですか、キャッシュとの間でデータをロード/永続化するために単一のロックを維持します

public static string LoadData(int id)
{
    if (_isDataLoaded[id - 1])
      return _data[id - 1];

    lock (_locker)
    {
        if (_isDataLoaded[id - 1])
          return _data[id - 1];

        if (File.Exists(_fileName))
        {
            bool dataExists;
            string cachedData;
            if (GetCachedConstant(id, out cachedData)) //read from a file
            {
                _data[id - 1] = cachedData;
                _isDataLoaded[id - 1] = true;
                return _data[id - 1];
            }
        }

        string remoteData = GetFromServer(id);
        CacheToFile(id, remoteData); //write to a file
        _data[id - 1] = remoteData;
        _isDataLoaded[id - 1] = true;
        return _data[id - 1];
    }
}
于 2012-11-22T16:05:13.917 に答える
1
    lock (_locker)
    {
        _isDataLoaded[id - 1] = true;
        _data[id - 1] = cachedData;
    }

_isDataLoadedこれはの前に設定されているため、前から見たり読んだりする_data人が初期化されるという競争があります。_isDataLoaded_data_data

しかし、それを逆にしても問題は解決しません。リーダーはロックやメモリバリアを使用しないため、別のスレッドが同じ順序で割り当てを表示するという保証はありません。

C#でそれを行う適切な方法については、ウィキペディアを確認してください。http://en.wikipedia.org/wiki/Double-checked_locking

于 2012-11-22T16:11:25.237 に答える
0

ダブルチェックロックの背後にある考え方は、その名前にあります。lock前に(コードのように)条件をチェックしますが、lockブロック内でもチェックして、現在のスレッドが(成功した)チェックとlock声明。

于 2012-11-22T15:54:11.680 に答える