在.NET中并发访问静态成员

VJA*_*JAI 5 .net c# concurrency multithreading locking

我有一个包含静态集合的类,用于将登录用户存储在ASP.NET MVC应用程序中.我只是想知道下面的代码是否是线程安全的.每当我向onlineUsers集合添加或删除项目时,是否需要锁定代码.

public class OnlineUsers
{
    private static List<string> onlineUsers = new List<string>();
    public static EventHandler<string> OnUserAdded;
    public static EventHandler<string> OnUserRemoved;

    private OnlineUsers()
    {
    }

    static OnlineUsers()
    {
    }

    public static int NoOfOnlineUsers
    {
        get
        {
            return onlineUsers.Count;
        }
    }

    public static List<string> GetUsers()
    {
        return onlineUsers;
    }

    public static void AddUser(string userName)
    {
        if (!onlineUsers.Contains(userName))
        {
            onlineUsers.Add(userName);

            if (OnUserAdded != null)
                OnUserAdded(null, userName);
        }
    }

    public static void RemoveUser(string userName)
    {
        if (onlineUsers.Contains(userName))
        {
            onlineUsers.Remove(userName);

            if (OnUserRemoved != null)
                OnUserRemoved(null, userName);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 13

这绝对不是线程安全的.任何时候2个线程正在做某事(在Web应用程序中很常见),混乱是可能的 - 异常或静默数据丢失.

是的,你需要某种同步,如lock; 和static通常用于数据存储,IMO一个非常糟糕的主意(除非非常小心地处理和有限的物像的配置数据).

此外 - static事件以使对象图意外存活的好方法而臭名昭着.也要谨慎对待; 如果您只订阅一次,很好 - 但不要按订阅订阅等.

此外 - 它不仅仅是锁定操作,因为这一行:

return onlineUsers;
Run Code Online (Sandbox Code Playgroud)

返回您的列表,现在不受保护.必须同步对项目的所有访问权限.就个人而言,我会返回一份副本,即

lock(syncObj) {
    return onlineUsers.ToArray();
}
Run Code Online (Sandbox Code Playgroud)

最后,.Count从这样做返回可能会令人困惑 - 因为它不能保证Count在任何时候.它仅在该时间点是信息性的.

  • 关于"Count"不可靠的观点可能是可变类型的多线程编程中最重要且经常被误解的方面.通常,你在单线程生活中推理代码的方式是内存是稳定的,除非你确实努力改变它.在多线程代码中,情况正好相反:除非你正在努力保持稳定,否则你必须假设一切都在不断变化.观察集合的大小*时,您将观察到过去的大小*.在计算它之后,它可能会立即突变一百万次. (3认同)
  • @Mark仔细对待他们很好; 但是如果你从一个*transient*对象订阅(生命很短的时间),但***忘记取消订阅***,你将永远保持该对象的存活.当您重复该过程时,它将导致内存增长,并将逐渐调用它越来越慢. (2认同)