2

XML ファイルを逆シリアル化し、そこから c# オブジェクトを生成するヘルパー関数があります。

その後、オブジェクトはサーバーのメモリに追加されます。サーバーのメモリに何かを追加する唯一の方法は、この関数を使用することだけです。

public class DeserializeXmlHelper
{
    public void DeserializeXml(Guid xml_Id, decimal version)
    {
        // heavy process here which takes about 3 seconds
    }
}

この関数は、API メソッド (Asp.net MVC API で作成) を使用してさまざまなクライアントによって呼び出されています。

API を呼び出すときに、他の誰かが同じパラメーターで同じ関数を既に呼び出している場合、関数の実行を防ぐことはできますか?

このようなものですが、それが良い方法かどうかはわかりません。

public class DeserializeXmlHelper
{
    private static readonly ConcurrentDictionary<string, object> _processes = new ConcurrentDictionary<string, object>();

    public void DeserializeXml(Guid xml_Id, decimal version)
    {
        string processKey = string.Format("{0}_v{1}", xml_Id, version.ToString("#0.0"));
        object processLocker = null;
        if (_processes.TryGetValue(processKey, out processLocker) == false)
        {
            processLocker = new object();
            _processes.TryAdd(processKey, processLocker);
        }

        lock (processLocker)
        {
            // heavy process here which takes about 3 seconds

            _processes.TryRemove(processKey);
        }
    }
}

編集済み - 新バージョン

ティム・ロジャーの答えはうまくいっています。

ただし、最初の呼び出しが終了したときにのみ戻りたい場合は、次のようにすることはできますか? (ロックを追加する方法がわからないため、ConcurrentDictionaryを使用していますが、考え方は同じはずです)

public class DeserializeXmlHelper
{
    private static readonly ConcurrentDictionary<string, string> _processes = new ConcurrentDictionary<string, string>();
    public void DeserializeXml(Guid xml_Id, decimal version)
    {
        string _processKey = string.Format("{0}_v{1}", xml_Id, version.ToString("#0.0"));
        string _processValue = null;
        if (_processes.TryGetValue(_processKey, out _processValue) == true)
        {
            // function already called with the same parameters
            do
            {
                System.Threading.Thread.Sleep(100);
            }
            while(_processes.TryGetValue(_processKey, out _processValue) == true)

            return;
        }

        try
        {
            _processes.TryAdd(_processKey, _processValue);

            var begin = "begin process";

            System.Threading.Thread.Sleep(10000);

            var end = "ending process";
        }
        finally
        {
            _processes.TryRemove(_processKey, out _processValue);
        }
    }
}
4

1 に答える 1

0

元のソリューションはそう遠くはありませんでしたが、現在の状態に関係なく、常にプロセスを実行しているように見えました.

コンカレント ディクショナリを使用した場合でも、状態を更新している間はディクショナリ全体をロックするロックも必要です。これは、 と の間でスレッドが中断される可能性があるためTryGetValueですAdd

private static readonly ConcurrentDictionary<string, object> _processes = new ConcurrentDictionary<string, object>();
private static readonly object _dictionaryLock = new object();

public void DeserializeXml(Guid xml_Id, decimal version)
{
    string processKey = string.Format("{0}_v{1}", xml_Id, version.ToString("#0.0"));
    object processLocker = null;
    bool needsExecuting = false;

    lock (_dictionaryLock)
    {
      if (_processes.TryGetValue(processKey, out processLocker) == false)
      {
          needsExecuting = true;
          processLocker = new object();
          _processes.Add(processKey, processLocker);
      }
    }

    lock (processLocker)
    {
        if (needsExecuting)
        {
          // heavy process here which takes about 3 seconds

          _processes.Remove(processKey);
        }
    }
}
于 2013-03-06T13:47:45.177 に答える