Apa yang harus ditambahkan untuk bagian pembaruan di ConcurrentDictionary AddOrUpdate

109

Saya mencoba menulis ulang beberapa kode menggunakan Dictionary untuk menggunakan ConcurrentDictionary. Saya telah meninjau beberapa contoh tetapi saya masih mengalami masalah dalam menerapkan fungsi AddOrUpdate. Ini adalah kode aslinya:

    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;

Saya tidak tahu apa yang harus ditambahkan untuk bagian pembaruan:

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

Setiap petunjuk akan dihargai.

pengguna438331
sumber

Jawaban:

220

Anda harus meneruskan a Funcyang mengembalikan nilai yang akan disimpan dalam kamus jika ada pembaruan. Saya kira dalam kasus Anda (karena Anda tidak membedakan antara tambah dan perbarui) ini akan menjadi:

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

Yaitu Funcselalu mengembalikan sessionId, sehingga Tambah dan Perbarui menetapkan nilai yang sama.

BTW: ada contoh di halaman MSDN .

M4N
sumber
4
Saya benar-benar berjuang untuk menemukan fungsi untuk menambah atau memperbarui nilai yang sama. thaks
Zapnologica
2
Jawaban yang bagus. Hanya dari tanda tangan AddOrUpdate () yang ditampilkan di Visual Studio Anda hanya dapat menebak arti dari 2 parameter. Namun dalam kasus khusus, yang ditanyakan oleh @ user438331, menurut saya solusi dalam jawaban saya menggunakan pengindeks sederhana lebih baik.
Niklas Peter
7
Seperti yang ditunjukkan @NiklasPeter ( stackoverflow.com/a/32796165/8479 ), lebih baik Anda menggunakan pengindeks biasa untuk menimpa nilainya, karena dalam kasus Anda, Anda tidak tertarik dengan nilai yang ada, jika ada. Jauh lebih mudah dibaca.
Rory
3
Sebaiknya ubah jawaban Anda untuk mengarahkan pengguna ke jawaban @NiklasPeter. Ini solusi yang jauh lebih baik.
Akankah Calderwood
63

Saya harap, saya tidak melewatkan apa pun dalam pertanyaan Anda, tetapi mengapa tidak seperti ini saja? Lebih mudah, atomic dan thread-safe (lihat di bawah).

userDic[authUser.UserId] = sessionId;

Menyimpan pasangan kunci / nilai ke dalam kamus tanpa syarat, menimpa nilai apa pun untuk kunci itu jika kuncinya sudah ada: Gunakan penyetel pengindeks

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

Pengindeks juga bersifat atomik. Jika Anda meneruskan suatu fungsi, itu mungkin bukan:

Semua operasi ini bersifat atomic dan aman untuk thread sehubungan dengan semua operasi lain di ConcurrentDictionary. Satu-satunya peringatan untuk atomicity setiap operasi adalah untuk mereka yang menerima delegasi, yaitu AddOrUpdate dan GetOrAdd. [...] delegasi ini dipanggil di luar kuncian

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

Niklas Peter
sumber
2
Ya, atom, itu terjadi sekaligus dan tidak bisa separuh terjadi atau terputus. Namun tidak aman karena orang lain dapat mengubahnya menjadi sesuatu yang lain sebelum Anda melakukannya, dalam hal ini ada perubahan yang hilang, dan Anda tidak tahu itu terjadi, jika Anda hanya ingin mengubahnya jika nilainya adalah apa yang Anda harapkan maka ini tidak akan melakukannya untuk Anda.
gelandangan
26

Saya akhirnya menerapkan metode ekstensi:

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);
    }
}
steve cook
sumber
1

Bagi mereka yang tertarik, saat ini saya mengimplementasikan kasus yang merupakan contoh bagus untuk menggunakan "oldValue" alias nilai yang ada alih-alih memaksakan yang baru (secara pribadi saya tidak suka istilah "oldValue" karena bukan itu lama ketika dibuat hanya beberapa kutu prosesor yang lalu dari dalam utas paralel).

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);
Nicolas
sumber
6
jika Anda tidak ingin mengubah nilai yang ada Anda harus menggunakan GetOrAdd()bukan msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
Rory
1
Hm ya, Anda benar, GetOrAdd () lebih sederhana dan cukup dalam kasus ini - terima kasih untuk petunjuk ini!
Nicolas