[英]C# variable that should never be null throws null reference exception
我發現了一個奇怪的情況,我無法解釋。 Basicaly 屬性“UserCalls”在開始時工作正常,但在應用程序啟動后很長時間(一個多月)后,不應取 null 值的屬性,在調用函數時拋出 null 異常(例如 Z83C8324F192E756CDE ))。
代碼示例:
public class UserCall
{
public long UserID { get; set; }
public long CallID { get; set; }
}
public static class Cache
{
private static List<UserCall> userCalls = new List<UserCall>();
public static List<UserCall> UserCalls
{
get
{
if (userCalls == null)
{
userCalls = new List<UserCall>();
}
return userCalls;
}
set
{
userCalls = value;
}
}
private static void AddCall(long userID, long callID)
{
UserCalls.Add(
new UserCall
{
CallID = callID,
UserID = userID
});
}
public static UserCall GetCall(long userID)
{
var userCall = UserCalls.FirstOrDefault(x => x.UserID == userID);
return userCall;
}
public static void RemoveCall(long userID)
{
UserCalls.RemoveAll(x => x.UserID == userID);
}
}
調用“GetCall”時拋出以下異常:
[ERROR] Object reference not set to an instance of an object. | at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
可以從多個線程調用方法“AddCall”、“GetCall”和“RemoveCall”。 此異常隨機發生(但僅在服務啟動后很長時間后)在更多負載的服務上發生。 如果發生異常,它會被鎖定在這個永久 null state 並且不會自行更正,只有服務重啟會有所幫助。 我知道這個實現並不完全是線程安全的,但它仍然不應該返回 null。 為什么會這樣? 有沒有人遇到過類似的情況?
在多線程上下文中,您的UserCalls
屬性可以是 null。
鑒於:
public static List<UserCall> UserCalls
{
get
{
if (userCalls == null)
{
userCalls = new List<UserCall>();
}
return userCalls;
}
set
{
userCalls = value;
}
}
考慮以下具有兩個線程的序列,[A] 和 [B],其中userCalls
當前為非空。
[A] Accesses `UserCalls` getter.
[A] Checks if `userCalls` is null. It is not, so it will skip the assignment.
[B] Accesses `UserCalls` setter, about to set it to `null`.
[B] Sets `userCalls` to null.
[A] Returns `userCalls`, which is now null => BANG!
您可以通過使用鎖來防止這種情況:
public static List<UserCall> UserCalls
{
get
{
lock (_locker)
{
if (userCalls == null)
{
userCalls = new List<UserCall>();
}
return userCalls;
}
}
set
{
lock (_locker)
{
userCalls = value;
}
}
}
static object _locker = new object();
但是,請注意,沒有什么可以阻止將null
UserCalls
如果這樣做,您還將在UserCalls.FirstOrDefault(x => x.UserID == userID);
等調用中獲得NullReferenceException
,因為x
將是 null 並且使用x.UserID
將為您提供 null 引用異常。
我建議:
添加鎖以使列表線程的使用安全
編碼:
public static class Cache
{
private static List<UserCall> userCalls = new List<UserCall>();
private static void AddCall(long userID, long callID)
{
lock(userCalls)
{
userCalls.Add(
new UserCall
{
CallID = callID,
UserID = userID
});
}
}
public static UserCall GetCall(long userID)
{
UserCall userCall = null;
lock(userCalls)
userCall = UserCalls.FirstOrDefault(x => x.UserID == userID);
return userCall;
}
public static void RemoveCall(long userID)
{
lock(userCalls)
userCalls.RemoveAll(x => x.UserID == userID);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.