ConcurrentDictionary
เป็นตัวเลือกที่ดีถ้ามันตอบสนองทุกความต้องการของด้ายความปลอดภัยของคุณ หากไม่เป็นเช่นนั้นกล่าวอีกนัยหนึ่งคือคุณกำลังทำอะไรที่ซับซ้อนเล็กน้อยDictionary
+ ปกติlock
อาจเป็นตัวเลือกที่ดีกว่า ตัวอย่างเช่นสมมติว่าคุณกำลังเพิ่มคำสั่งซื้อบางรายการลงในพจนานุกรมและคุณต้องการอัปเดตจำนวนคำสั่งซื้อทั้งหมด คุณสามารถเขียนโค้ดดังนี้:
private ConcurrentDictionary<int, Order> _dict;
private Decimal _totalAmount = 0;
while (await enumerator.MoveNextAsync())
{
Order order = enumerator.Current;
_dict.TryAdd(order.Id, order);
_totalAmount += order.Amount;
}
รหัสนี้ไม่ปลอดภัยสำหรับเธรด เธรดหลายเธรดที่อัปเดต_totalAmount
ฟิลด์อาจปล่อยให้อยู่ในสถานะที่เสียหาย ดังนั้นคุณอาจพยายามป้องกันโดยlock
:
_dict.TryAdd(order.Id, order);
lock (_locker) _totalAmount += order.Amount;
รหัสนี้ "ปลอดภัยกว่า" แต่ยังไม่ปลอดภัยต่อเธรด ไม่มีการรับประกันว่า_totalAmount
จะสอดคล้องกับรายการในพจนานุกรม เธรดอื่นอาจพยายามอ่านค่าเหล่านี้เพื่ออัปเดตองค์ประกอบ UI:
Decimal totalAmount
lock (_locker) totalAmount = _totalAmount
UpdateUI(_dict.Count, totalAmount)
totalAmount
อาจไม่ตรงกับการนับจำนวนของการสั่งซื้อในพจนานุกรม สถิติที่แสดงอาจผิดพลาด ณ จุดนี้คุณจะทราบว่าคุณต้องขยายการlock
ป้องกันเพื่อรวมการอัปเดตพจนานุกรม:
lock (_locker)
{
_dict.TryAdd(order.Id, order);
_totalAmount += order.Amount;
}
int ordersCount;
Decimal totalAmount;
lock (_locker)
{
ordersCount = _dict.Count;
totalAmount = _totalAmount;
}
UpdateUI(ordersCount, totalAmount);
รหัสนี้ปลอดภัยอย่างสมบูรณ์ แต่ประโยชน์ทั้งหมดของการใช้ a ConcurrentDictionary
จะหายไป
- ประสิทธิภาพการทำงานแย่ลงกว่าการใช้แบบธรรมดา
Dictionary
เนื่องจากการล็อคภายในภายในConcurrentDictionary
ตอนนี้สิ้นเปลืองและซ้ำซ้อน
- คุณต้องตรวจสอบโค้ดทั้งหมดของคุณสำหรับการใช้งานตัวแปรที่แชร์โดยไม่มีการป้องกัน
- คุณติดอยู่กับการใช้ API ที่น่าอึดอัดใจ (
TryAdd
?, AddOrUpdate
?)
ดังนั้นคำแนะนำของฉันคือ: เริ่มต้นด้วยDictionary
+ lock
และเก็บตัวเลือกในการอัปเกรดในภายหลังConcurrentDictionary
เป็นการเพิ่มประสิทธิภาพการทำงานหากตัวเลือกนี้ใช้งานได้จริง ในหลาย ๆ กรณีจะไม่เป็นเช่นนั้น