私有成员变量在类后面的 ASP.NET 代码中真的是线程安全的吗?

发布于 12-14 15:29 字数 1559 浏览 1 评论 0原文

我看到一个随机异常“集合已修改;枚举可能无法执行”- InvalidOperationException。

该异常指向下面代码片段中的 foreach 行,我知道在枚举时修改集合时会发生这种情况。

但是在我的场景中,我看不到发生这种情况的真正机会 - 除非私有成员不是线程安全的。我可能是错的,但这就是我需要帮助来理解和弄清楚的地方

这是我的代码的样子

我的类后面有代码,它有一个私有集合,例如

private Dictionary<string, string> _someDictionary = SomeConstantClass.ConstantValue;

在页面预渲染完成事件中,我枚举字典

protected override void OnPagePreRenderComplete(object sender, EventArgs e){
     _someDictionary["AnotherKey"] = "Another value";

     foreach(var dataValuePair in _SomeDictionary){
         //Do some operation
     }
}

我还有一个可以修改此集合的公共属性,但它在 ascx 文件中设置,例如

<tc: UserControlA runat="server" id="abc" CustomProperty="true" />

和这是它的实现,

public bool CustomProperty{
    set{
         if (value)
            _someDictionary["CustomProperty"] = "Custom Value";
    }
}

它确实修改了我的成员变量集合 - 但根据我的理解,这个属性应该在 Control Init 本身中触发并完成。

因此,我仍然没有看到在预渲染完成事件期间修改集合的情况。

知道什么可能导致异常发生吗?

其他说明:该页面当然有许多更新面板,尽管此特定的用户控件没有做任何花哨的事情,甚至没有回发场景。 从日志中我看到问题发生在对页面的 HTTP GET 请求中。

此外:建议我一种重现此问题的方法(如果有的话)。

对于有兴趣了解 SomeConstantClass.ConstantValue 的朋友,这里是

class SomeConstantClass{
  public static Dictionary<string, string> ConstantValue = new Dictionary<string, string> {
                      {"ABCD", "EFGH"},
                      {"HIJK", "LMNO"}
                   };
  }

I am seeing a random exception "Collection was modified; enumeration may not execute" - InvalidOperationException.

The exception points to foreach line in the code snippet below, I know this happens when a collection is modified while enumerating.

However in my scenario, I don't see a real chance of it happening - UNLESS private member is not thread safe.. I may be wrong, but this where I need help to understand and figure out.

Here is how my code looks

I have code behind class which has a private collection like

private Dictionary<string, string> _someDictionary = SomeConstantClass.ConstantValue;

In the page prerender complete event, I am enumerating the dictionary

protected override void OnPagePreRenderComplete(object sender, EventArgs e){
     _someDictionary["AnotherKey"] = "Another value";

     foreach(var dataValuePair in _SomeDictionary){
         //Do some operation
     }
}

I also have a public property which can modify this collection, but it set in the ascx file like

<tc: UserControlA runat="server" id="abc" CustomProperty="true" />

and here is its implementation,

public bool CustomProperty{
    set{
         if (value)
            _someDictionary["CustomProperty"] = "Custom Value";
    }
}

It certainly modified my member variable collection - But as per my understanding this property should be fired and done in the Control Init itself.

So, I still dont see a scenario where the collection is modified during the pre render complete event.

Any idea what could cause the exception to occur??

other notes: the page certainly has many update panels though this specific usercontrol does not do anything fancy and does not even have postback scenario.
From the log I see that the issue is happening in HTTP GET request to the page.

Moreover: Suggest me a way (if any) to reproduce this.

For my friends who were interested to know the SomeConstantClass.ConstantValue, here it is

class SomeConstantClass{
  public static Dictionary<string, string> ConstantValue = new Dictionary<string, string> {
                      {"ABCD", "EFGH"},
                      {"HIJK", "LMNO"}
                   };
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

沉溺在你眼里的海2024-12-21 15:29:55

如果您从 SomeConstantClass.ConstantValue 返回相同的实例,则多个页面将具有指向同一对象的私有成员变量。这将导致对象在一个页面的初始化时发生更改,同时在另一页面的 OnPagePreRenderComplete 上进行迭代。

确保在每次访问 SomeConstantClass.ConstantValue 时返回字典的新实例。示例:

public static Dictionary<string, string> ConstantValue
{
    get
    {
        return new Dictionary<string, string>
        {
            {"ABCD", "EFGH"},
            {"HIJK", "LMNO"}
        };
    }
}

这样每个页面都有自己的字典对象可以使用。这是快速的解决方案,您可以重构逻辑,这样您就不需要为每个页面创建新的字典。

基本上,私有成员变量只有在引用该页面私有的对象并且没有其他人知道该对象或者该对象本身被设计为线程安全的情况下才是线程安全的。通过私有成员封装对非线程安全静态对象的访问不会使其成为线程安全的。

If you are returning the same instance from SomeConstantClass.ConstantValue then multiple pages will have the private member variable pointing to the same object. This will lead to the object being changed on the init of one page while its being iterated on the OnPagePreRenderComplete of another page.

Make sure that you return a new instance of the dictionary in each access to SomeConstantClass.ConstantValue. Example:

public static Dictionary<string, string> ConstantValue
{
    get
    {
        return new Dictionary<string, string>
        {
            {"ABCD", "EFGH"},
            {"HIJK", "LMNO"}
        };
    }
}

This way each page will have it own dictionary object to work with. This is the quick solution, you could refactor the logic so that you don't need to have to create a new dictionary for each page.

Basically private member variables are only thread safe if they reference an object that is private to that page and no one else knows about that object or the object itself is designed to be thread-safe. Encapsulating access to a non thread-safe static object through a private member will not make it thread safe.

小嗲2024-12-21 15:29:55

只要您知道您没有在请求之间共享字典(或者您故意这样做),您就可以跳过尝试找出原因,而只需使用 并发字典,线程安全。仅仅因为字典是私有的并不意味着它是线程安全的。

As long as you know you're not sharing the dictionary across requests (or you are purposely doing that) you can skip trying to figure out why, and just use a Concurrent Dictionary which is thread safe. Just because a Dictionary is private doesn't make it thread safe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文