สิ่งที่ต้องเพิ่มสำหรับส่วนการอัพเดตใน ConcurrentDictionary AddOrUpdate


109

ฉันพยายามเขียนโค้ดใหม่โดยใช้ Dictionary เพื่อใช้ ConcurrentDictionary ฉันได้ตรวจสอบตัวอย่างบางส่วนแล้ว แต่ยังประสบปัญหาในการใช้งานฟังก์ชัน AddOrUpdate นี่คือรหัสดั้งเดิม:

    dynamic a = HttpContext;
    Dictionary<int, string> userDic = this.HttpContext.Application["UserSessionList"] as Dictionary<int, String>;

   if (userDic != null)
   {
      if (useDic.ContainsKey(authUser.UserId))
      {
        userDic.Remove(authUser.UserId);
      }
   }
  else
  {
     userDic = new Dictionary<int,string>();
  }
  userDic.Add(authUser.UserId, a.Session.SessionID.ToString());
  this.HttpContext.Application["UserDic"] = userDic;

ฉันไม่รู้ว่าจะเพิ่มอะไรในส่วนการอัปเดต:

userDic.AddOrUpdate(authUser.UserId,
                    a.Session.SessionID.ToString(),
                    /*** what to add here? ***/);

คำแนะนำใด ๆ จะได้รับการชื่นชม

คำตอบ:


220

คุณต้องผ่านFuncซึ่งส่งคืนค่าที่จะเก็บไว้ในพจนานุกรมในกรณีที่มีการอัปเดต ฉันเดาว่าในกรณีของคุณ (เนื่องจากคุณไม่ได้แยกแยะระหว่างการเพิ่มและการอัปเดต) สิ่งนี้จะเป็น:

var sessionId = a.Session.SessionID.ToString();
userDic.AddOrUpdate(
  authUser.UserId,
  sessionId,
  (key, oldValue) => sessionId);

กล่าวคือFuncส่งคืน sessionId เสมอเพื่อให้ทั้ง Add และ Update ตั้งค่าเดียวกัน

BTW: มีตัวอย่างบนหน้า MSDN


4
ฉันต่อสู้อย่างจริงจังเพื่อเพียงแค่ค้นหาฟังก์ชันที่จะเพิ่มหรืออัปเดตเป็นค่าเดียวกัน ท่า
Zapnologica

2
คำตอบที่ดี. จากลายเซ็นของ AddOrUpdate () ที่แสดงใน Visual Studio คุณสามารถเดาความหมายของพารามิเตอร์ 2 ตัวได้เท่านั้น อย่างไรก็ตามในกรณีเฉพาะที่ @ user438331 ถามเกี่ยวกับฉันคิดว่าวิธีแก้ปัญหาในคำตอบของฉันโดยใช้ตัวสร้างดัชนีอย่างง่ายนั้นดีกว่า
Niklas Peter

7
ดังที่ @NiklasPeter ชี้ให้เห็น ( stackoverflow.com/a/32796165/8479 ) คุณจะดีกว่าเพียงแค่ใช้ตัวสร้างดัชนีปกติเพื่อเขียนทับค่าเนื่องจากในกรณีของคุณคุณจะไม่สนใจค่าที่มีอยู่หากมี อ่านได้มากขึ้น
รอรี

3
ฉันขอแนะนำให้เปลี่ยนคำตอบของคุณเพื่อชี้ให้ผู้ใช้ทราบคำตอบของ @NiklasPeter เป็นทางออกที่ดีกว่ามาก
Will Calderwood

63

ฉันหวังว่าฉันจะไม่พลาดอะไรในคำถามของคุณ แต่ทำไมไม่เป็นแบบนี้ล่ะ ง่ายกว่าอะตอมและเธรดปลอดภัย (ดูด้านล่าง)

userDic[authUser.UserId] = sessionId;

จัดเก็บคู่คีย์ / ค่าไว้ในพจนานุกรมโดยไม่มีเงื่อนไขเขียนทับค่าใด ๆ สำหรับคีย์นั้นหากคีย์นั้นมีอยู่แล้ว: ใช้ตัวตั้งค่าของดัชนี

(ดู: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx )

ตัวสร้างดัชนีก็คือปรมาณูเช่นกัน หากคุณส่งผ่านฟังก์ชันแทนอาจไม่ใช่:

การดำเนินการทั้งหมดนี้เป็นแบบปรมาณูและปลอดภัยต่อเธรดสำหรับการดำเนินการอื่น ๆ ทั้งหมดบน ConcurrentDictionary ข้อแม้เดียวของ atomicity ของแต่ละการดำเนินการคือสำหรับผู้ที่ยอมรับผู้รับมอบสิทธิ์คือ AddOrUpdate และ GetOrAdd [... ] ผู้ได้รับมอบหมายเหล่านี้ถูกเรียกออกไปนอกล็อก

ดู: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx


2
ใช่ปรมาณูที่มันเกิดขึ้นพร้อมกันและไม่สามารถเกิดขึ้นครึ่งหนึ่งหรือหยุดชะงักได้ อย่างไรก็ตามไม่ปลอดภัยที่คนอื่นสามารถเปลี่ยนเป็นอย่างอื่นได้ก่อนที่คุณจะทำซึ่งในกรณีนี้การเปลี่ยนแปลงจะหายไปและคุณไม่รู้ว่ามันเกิดขึ้นหากคุณต้องการเปลี่ยนก็ต่อเมื่อค่าเป็นอย่างที่คุณคาดหวัง สิ่งนี้จะไม่ทำเพื่อคุณ
trampster

26

ฉันลงเอยด้วยการใช้วิธีการขยาย:

static class ExtensionMethods
{
    // Either Add or overwrite
    public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
    {
        dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
    }
}

1

สำหรับผู้ที่สนใจตอนนี้ฉันกำลังใช้เคสซึ่งเป็นตัวอย่างที่ดีในการใช้ค่า "oldValue" หรือที่เรียกว่าค่าที่มีอยู่แทนที่จะบังคับค่าใหม่ (โดยส่วนตัวฉันไม่ชอบคำว่า "oldValue" เนื่องจากไม่ใช่อย่างนั้น เก่าเมื่อสร้างโปรเซสเซอร์เพียงไม่กี่ครั้งที่ผ่านมาจากภายในเธรดคู่ขนาน)

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);

6
หากคุณไม่ต้องการเปลี่ยนค่าที่มีอยู่คุณควรใช้GetOrAdd()แทนmsdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Rory

1
อืมใช่คุณพูดถูก GetOrAdd () ง่ายกว่าและเพียงพอในกรณีนี้ - ขอบคุณสำหรับคำใบ้นี้!
Nicolas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.