عنوان:

‫مورد عجیب NET ConcurrentDictionary. و Closures


نویسنده: وحید نصیری
تاریخ: ۱۴۰۳/۱۲/۰۶ ۰۸:۲۵
آدرس: www.dntips.ir
این مقاله closures در #C و نحوه‌ی اجتناب از آنها هنگام استفاده از ConcurrentDictionary.GetOrAdd را توضیح می‌دهد. Closures می‌توانند باعث تخصیص‌های ناخواسته، مشکلات همزمانی و نشت حافظه شوند.

Closures چیست؟

  • هنگامیکه یک تابع lambda از متغیری خارج از محدوده‌ی مستقیم خود استفاده می‌کند، closures رخ می‌دهند.
  • این کار باعث "گرفتن" متغیر شده و به طور بالقوه، منجر به بروز مشکلاتی می‌شود.

void SayHello(string name)
{
    var hello = () => 
    {
        Console.WriteLine($"Hello {name}"); // name گرفته شده است
    };
    hello();
}
مشکلات closures:
  • تخصیص‌های اضافی.
  • احتمال تغییر ناخواسته وضعیت.
  • نشتی احتمالی حافظه.
راه حل: وضعیت مورد نیاز را به عنوان آرگومان، به lambda ارسال کنید.


ConcurrentDictionary.GetOrAdd و Closures

  • متد ConcurrentDictionary.GetOrAdd اگر به درستی استفاده نشود، می‌تواند closures ایجاد کند.
  • مثال (ایجاد یک closure):
var result = concurrentDictionary.GetOrAdd(key, (k) => {
    Console.WriteLine($"Building {k}");
    return new Item(value, DateTime.Now); // value گرفته شده است
});
  • متغیر value، گرفته شده و یک closure ایجاد می‌کند.

اجتناب از Closures با Overload متد GetOrAdd

  • ConcurrentDictionary.GetOrAdd یک overload دارد که از closures جلوگیری می‌کند:
var result = concurrentDictionary.GetOrAdd(
    key: key,
    valueFactory: (k, arg) => // arg آرگومان factory را دریافت می‌کند
    {
        Console.WriteLine($"Building {k}");
        return new Item(arg, DateTime.Now);
    },
    factoryArgument: value); // مستقیماً به عنوان یک آرگومان ارسال می‌شود
این overload موارد زیر را می‌گیرد:
    • کلید.
    • یک lambda که کلید و یک آرگومان factory را می‌گیرد.
    • خود آرگومان factory.
  • این رویکرد از گرفتن متغیرها جلوگیری می‌کند و closure را حذف می‌کند. آرگومان‌های متعدد باید در یک کلاس کانتینر پیچیده شوند.
  • استفاده از این overload عملکرد را بهبود می‌بخشد و از مشکلات احتمالی همزمانی/حافظه جلوگیری می‌کند.



مشاهده مطلب اصلی