نمط Singleton الآمن للخيوط (Thread-Safe)
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 52 من 70.
يضمن نمط Singleton أن الفئة (class) لديها مثيل واحد فقط في جميع أنحاء تطبيقك ويوفر نقطة وصول عالمية إليه. هذا مفيد لإدارة الموارد المشتركة مثل إعدادات التكوين، أو خدمات تسجيل الأحداث، أو اتصالات قاعدة البيانات.
يستخدم نمط Singleton الأساسي منشئًا خاصًا لمنع الإنشاء الخارجي وخاصية ثابتة لتوفير الوصول:
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // منشئ خاص
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}تضمن عبارة lock سلامة الخيوط (thread safety) - إذا حاولت خيوط متعددة الوصول إلى Instance في وقت واحد، يمكن لخيط واحد فقط الدخول إلى القسم الحرج في كل مرة. بدون ذلك، يمكن لخيطين رؤية _instance كقيمة فارغة (null) وإنشاء مثيلات منفصلة.
تقدم لغة C# نهجاً أبسط وآمناً من حيث الخيوط (thread-safe) باستخدام التهيئة الثابتة (static initialization):
public class Logger
{
private static readonly Logger _instance = new Logger();
private Logger() { }
public static Logger Instance => _instance;
public void Log(string message) => Console.WriteLine(message);
}هذا الإصدار آمن من حيث تعدد الخيوط (thread-safe) لأن CLR يضمن أن تهيئة الحقول الثابتة (static field initialization) تحدث مرة واحدة فقط. يضمن كلا النهجين حصولك دائمًا على نفس المثيل (instance):
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");التحدي
سهللنقم ببناء مدير إعدادات آمن للخيوط (thread-safe) باستخدام نمط Singleton. في التطبيقات الحقيقية، يجب أن تكون إعدادات التكوين قابلة للوصول في جميع أنحاء البرنامج مع ضمان وجود مثيل واحد فقط - وهي حالة استخدام مثالية لنمط Singleton.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
ConfigManager.cs: قم بإنشاء فئةConfigManagerفي مساحة الأسماءConfigurationالتي تطبق نمط Singleton. يجب أن يقوم مدير الإعدادات الخاص بك بما يلي:- امتلاك حقل ثابت خاص (private static field) للاحتفاظ بالمثيل الوحيد
- استخدام منشئ خاص (private constructor) لمنع إنشاء مثيلات خارجية
- توفير خاصية
Instanceثابتة تعيد المثيل الوحيد (استخدم نهج التهيئة الثابتة الأبسط لضمان أمان الخيوط) - تضمين حقل نصي خاص
_appNameتمت تهيئته بـ"MyApp" - امتلاك طريقة
SetAppName(string name)لتحديث اسم التطبيق - امتلاك طريقة
GetAppName()التي تعيد اسم التطبيق الحالي
Program.cs: في ملفك الرئيسي، أثبت أن Singleton يعمل بشكل صحيح من خلال الوصول إلى المثيل عدة مرات وإظهار أن التغييرات تستمر عبر جميع المراجع.
ستتلقى مدخلاً واحداً:
- اسم تطبيق جديد لتعيينه (على سبيل المثال،
ProductionServer)
في برنامجك:
- احصل على مثيل
ConfigManagerواطبع اسم التطبيق الافتراضي - احصل على المثيل مرة أخرى (في متغير مختلف) واستخدمه لتعيين اسم التطبيق الجديد من المدخلات
- احصل على المثيل للمرة الثالثة واطبع اسم التطبيق لإثبات أن جميع المراجع تشترك في نفس البيانات
على سبيل المثال، إذا كان الإدخال هو ProductionServer، فيجب أن يكون الناتج:
MyApp
ProductionServerيوضح هذا الفائدة الأساسية لنمط Singleton - بغض النظر عن عدد المرات التي تصل فيها إلى Instance، فإنك تحصل دائماً على نفس الكائن بحالة مشتركة. يضمن نهج التهيئة الثابتة أمان الخيوط لأن CLR يضمن تهيئة الحقل الثابت مرة واحدة فقط، حتى لو وصلت إليه خيوط متعددة في وقت واحد.
ورقة مرجعية
يضمن نمط Singleton أن الفئة (class) لديها مثيل واحد فقط طوال تطبيقك ويوفر نقطة وصول عالمية إليه.
نمط Singleton أساسي مع قفل صريح (explicit locking) لضمان سلامة الخيوط (thread safety):
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // Private constructor
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}تضمن عبارة lock سلامة الخيوط (thread safety) من خلال السماح لخيط واحد فقط بالدخول إلى القسم الحرج (critical section) في المرة الواحدة.
نمط Singleton أبسط وآمن للخيوط (thread-safe) باستخدام التهيئة الثابتة (static initialization):
public class Logger
{
private static readonly Logger _instance = new Logger();
private Logger() { }
public static Logger Instance => _instance;
public void Log(string message) => Console.WriteLine(message);
}هذا النهج آمن للخيوط (thread-safe) لأن CLR يضمن حدوث تهيئة الحقول الثابتة (static field initialization) مرة واحدة فقط.
الاستخدام - يعيد دائماً نفس المثيل:
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");جرّب بنفسك
using System;
using Configuration;
class Program
{
public static void Main(string[] args)
{
// قراءة المدخلات - اسم التطبيق الجديد
string newAppName = Console.ReadLine();
// TODO: احصل على مثيل ConfigManager واطبع اسم التطبيق الافتراضي
// TODO: احصل على المثيل مرة أخرى (في متغير مختلف) واستخدمه لتعيين اسم التطبيق الجديد
// TODO: احصل على المثيل للمرة الثالثة واطبع اسم التطبيق لإثبات أن جميع المراجع تشترك في نفس البيانات
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List10أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singleton الآمن للخيوط (Thread-Safe)نمط المصنع (Factory Pattern)نمط المراقب (Events)نمط الاستراتيجية (Strategy Pattern)2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injection