代码之家  ›  专栏  ›  技术社区  ›  Viking

当字典为只读且从未更新时,这段代码如何抛出KeyNotFoundException!?大部分时间都很好

  •  -2
  • Viking  · 技术社区  · 7 年前

    编辑:大多数情况下它工作正常错误是异常的。

    在调用此方法的行上,错误报告说它抛出了KeyNotFoundException“im mapping an enum to a string(!在声明时,我没有以任何方式更新字典!):

    public Task<bool> UpdateStatusAsync(Some object, Status status)
    {
        var somerequest = new Somerequest 
        {
            ...
            Status = MapDomainStatusToAclStatus(status.Type),
            ...
        };
    
        .............
    }
    

    下面是映射函数:

    private string MapDomainStatusToAclStatus(DomainStatus domainStatus)
    {
        if (DictionaryDomainACLStatus.ContainsKey(domainStatus))
        {
            return DictionaryDomainACLStatus[domainStatus];
        }
    
        // Some refactor todo comment.
        switch (domainStatus)
        {
            case DomainStatus.Aborted:
                return "Some string";
        }
    
        Log.Info($"[MapDomainStatusToAclStatus] DomainStatus={domainStatus} cant be mapped to ACL status");
        return String.Empty;
    }
    

    有可能吗?

    编辑:

    由于我得到了一些关于可能的种族状况的答复,我想补充一点,字典声明如下:

     public static readonly Dictionary<DomainStatus, string> { values }
    

    编辑2:我的字典声明:

    public static readonly Dictionary<DomainStatus, string> DictionaryDomainACLStatus= new Dictionary<DomainStatus, string>
        {
                {DomainStatus.Approved, "TEXT" },
                {DomainStatus.Declined, "TEXT2" }
        };
    

    稍后代码中不会执行创建更新删除操作。

    3 回复  |  直到 7 年前
        1
  •  1
  •   Darren Lewis    7 年前

    静态字典是在所有线程之间共享的,因此不是线程安全的。检查它是否真的需要是静态的,或者查看 ConcurrentDictionary 这是线程安全的。

        2
  •  1
  •   Sefe    7 年前

    您的问题是您在并发代码中,这意味着多个代码线程可能同时运行。所以你必须确保并发操作 原子的 也就是说,在多线程环境中,操作总是作为一个步骤执行,而另一个线程不能中断它。

    密码。。。

    if (DictionaryDomainACLStatus.ContainsKey(domainStatus))
    {
        return DictionaryDomainACLStatus[domainStatus];
    }
    

    …显然不是原子的,因为它由两个独立的步骤组成。介于 ContainsKey 索引器,另一个线程可以修改字典,这将导致 KeyNotFoundException 如果修改是 Remove .

    首先想到的是用。。。

    if (DictionaryDomainACLStatus.TryGetValue(domainStatus, out string value)) {
        return value;
    }
    

    不过,这对你没有帮助,因为 TryGetValue 它本身不是原子的,这意味着它可以被另一个线程在内部中断。

    解决这个问题的一个常见方法是 临界截面 用一个 lock :

    lock (myCriticalSection) {
        if (DictionaryDomainACLStatus.ContainsKey(domainStatus))
        {
            return DictionaryDomainACLStatus[domainStatus];
        }
    }
    

    词典的任何访问都必须在 在同一关键部分。

    另一种选择是使用 ConcurrentDictionary ,它提供原子操作。不过,只有在 全部的 对字典的访问可以在一个(原子)步骤中执行。这就是为什么通常你会更好的锁。

        3
  •  0
  •   Viking    7 年前

    我发现错误存在于另一段代码中,但我忽略了它。谢谢大家和我一起思考。在调试时,我肯定会更精确。