مشكلة سباق البيانات
افهم سبب عدم أمان التعديل المتزامن.
مشكلة سباق البيانات درس مجاني في Swift Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Swift Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Swift Academy 4 دروس في المجموع.
ما هو سباق البيانات؟
يحدث سباق البيانات عندما يصل خيطان أو أكثر إلى موقع الذاكرة نفسه في الوقت ذاته، ويكون أحد عمليات الوصول على الأقل عملية كتابة، من دون وجود مزامنة بينها.
تكون النتيجة سلوكًا غير معرّف: قيمًا تالفة أو حالات تعطل أو أخطاء لا تظهر إلا تحت الحمل.
الحالة القابلة للتغيير المشتركة
السبب الجذري لسباقات البيانات هو الحالة القابلة للتغيير المشتركة. فإذا كان بإمكان عدة مهام قراءة المتغير نفسه وكتابته، يصبح ترتيب العمليات غير متوقع.
قد يفقد العداد أدناه بعض الزيادات لأن count += 1 عملية قراءة-تعديل-كتابة وليست ذرية.
final class Counter {
var count = 0
func increment() {
count += 1 // read, add, write: not atomic
}
}سبب فقدان الزيادات
تُترجم العبارة count += 1 إلى ثلاث خطوات: تحميل القيمة، وإضافة واحد، وتخزينها مجددًا.
إذا حمّل خيطان القيمة 5 في الوقت نفسه، فسيخزّن كلاهما القيمة 6، فتختفي إحدى الزيادتين.
// Thread A loads 5
// Thread B loads 5
// Thread A stores 6
// Thread B stores 6 <-- lost updateتمزق القيم الأكبر
إضافةً إلى فقدان التحديثات، يمكن للكتابات المتزامنة إلى القيم متعددة الكلمات، مثل بنية أو قيمة 64-bit على بعض المنصات، أن تسبب تمزقًا؛ إذ يقرأ القارئ نصف إحدى الكتابتين ونصف الكتابة الأخرى.
struct Point { var x: Double; var y: Double }
var p = Point(x: 0, y: 0)
// Concurrent writes may leave x from one write and y from anotherالإصلاح القديم: الأقفال
قبل Swift Concurrency، كان العلاج التقليدي هو استخدام قفل (mutex). ولا يحتفظ بالقفل في الوقت نفسه إلا خيط واحد، مما يجعل الوصول تسلسليًا.
تعمل الأقفال، لكن من السهل إساءة استخدامها: نسيان إلغاء القفل، وحالات الجمود، وانعكاس الأولوية.
import Foundation
final class SafeCounter {
private let lock = NSLock()
private var count = 0
func increment() {
lock.lock()
defer { lock.unlock() }
count += 1
}
}قوائم Dispatch التسلسلية
تتمثل طريقة تقليدية أخرى في استخدام قائمة Dispatch تسلسلية. إذ تُمرر جميع عمليات التغيير إلى قائمة واحدة، فلا تتداخل أبدًا.
import Foundation
final class QueueCounter {
private let queue = DispatchQueue(label: "counter")
private var count = 0
func increment() {
queue.async { self.count += 1 }
}
}سبب هشاشة المزامنة اليدوية
تعتمد الأقفال والقوائم على الانضباط. ولا يتحقق المترجم من حماية كل عملية وصول.
يكفي أن تمر قراءة واحدة بلا حماية حتى يعود السباق. ولا يوجد ضمان في وقت الترجمة.
// Nothing stops a careless reader from doing this:
// let value = counter.count // unsynchronized read = raceتغيّر Swift Concurrency قواعد اللعبة
تجعل Swift Concurrency أمان سباقات البيانات ميزة في اللغة بدلًا من كونه مجرد اتفاقية.
تتعاون ثلاث أدوات: actor لحماية الحالة القابلة للتغيير، وSendable للأنواع الآمنة للمشاركة، والمترجم لفرض كليهما.
actor Counter {
private var count = 0
func increment() { count += 1 }
}تفرض actors تسلسل الوصول
يضمن actor تشغيل مهمة واحدة فقط للكود الذي يغيّر حالته في الوقت ذاته. ويتولى وقت التشغيل تسلسل الوصول تلقائيًا.
لن تكتب قفلًا أبدًا؛ فنموذج actor يوفر المزامنة.
actor BankAccount {
private(set) var balance = 0
func deposit(_ amount: Int) { balance += amount }
}الفرض في وقت الترجمة
يرفض المترجم السماح لك بالوصول إلى الحالة المعزولة بواسطة actor من دون المرور عبر actor. وتصبح الاستدعاءات بين actors غير متزامنة (await).
وهكذا تتحول السباقات أثناء التشغيل إلى أخطاء في وقت الترجمة.
let account = BankAccount()
// Must await: balance is actor-isolated
// let b = await account.balanceيحدّد Sendable ما يعبر الخيوط
تحدد protocol Sendable الأنواع الآمنة للتمرير عبر حدود التزامن.
يمنع المترجم إرسال حالة قابلة للتغيير وغير Sendable إلى مهمة أخرى، مما يغلق مصدر السباق على مستوى النوع.
struct Money: Sendable {
let amount: Int
let currency: String
}تحقق سريع: سباقات البيانات
اختبر مدى فهمك لما يسبب سباق البيانات.
مراجعة: مشكلة سباق البيانات
تنشأ سباقات البيانات من الوصول المتزامن إلى حالة قابلة للتغيير مشتركة من دون مزامنة، مما يؤدي إلى فقدان التحديثات وتمزق القيم وسلوك غير معرّف.
تعمل الإصلاحات القديمة، مثل الأقفال والقوائم التسلسلية، لكنها غير خاضعة للتحقق وهشة. وتستبدل Swift Concurrency الاتفاقية بالفرض: يعزل actor الحالة، ويحدّد Sendable ما يعبر الحدود، ويتحقق المترجم من الأمان. ويستكشف باقي هذه الدورة تلك الأدوات بعمق.
الأسئلة الشائعة
هل درس «مشكلة سباق البيانات» مجاني؟
نعم — نص درس «مشكلة سباق البيانات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Swift Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Swift Academy 4 دروس في المجموع.
ماذا ستتعلم في «مشكلة سباق البيانات»؟
افهم سبب عدم أمان التعديل المتزامن. تتمرن على Swift Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Swift Academy؟
لا تُشترط خبرة سابقة. Swift Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «مشكلة سباق البيانات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Swift Academy هذا؟
نعم. كل درس في Swift Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- مشكلة سباق البيانات
- بروتوكول Sendable
- عزل Actor و nonisolated
- الترحيل إلى التزامن الصارم