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 回答