أكبر خطأ يقع فيه المبتدئون في Go هو الخلط بين التزامن السهل والتزامن المجاني.
Goroutines خفيفة الوزن، وهو أمر رائع. لكنها لا تزال تتطلب موارد، ولا تزال تستهلك وحدة المعالجة المركزية والذاكرة والمنافذ ووصف الملفات بشكل غير مباشر من خلال المهام التي تقوم بها.
## المشكلة مع الإفراط غير المحدود
هذا جيد في النطاق الصغير:
```go
for _, file := range files {
go downloadAndParse(file)
}
يصبح الأمر خطيرًا عندما يعكس حجم الحلقة إدخالًا خارجيًا أو تراكمًا كبيرًا.
الفشل المعتاد ليس "عدد كبير جدًا من goroutines" كرقم مجرد. بل هو استنفاد على المدى الطويل:
- عدد كبير جدًا من الاتصالات المفتوحة
- ضغط كبير جدًا على إدخال/إخراج
- كمية كبيرة جدًا من الذاكرة المتداولة
لماذا تساعد مجمعات العمال
تقوم مجمعات العمال بإنشاء ضغط خلفي من خلال تحديد مقدار العمل الذي يمكن أن يتم تنفيذه بالتزامن:
func worker(jobs <-chan string, results chan<- error) {
for file := range jobs {
results <- downloadAndParse(file)
}
}
النقطة هي التحكم، وليس الأسلوب.
أنت تقرر مقدار العمل الذي يجب أن يقبله النظام في وقت واحد، بناءً على الاختناق الحقيقي.
القنوات تتعلق بالتنسيق
تعتبر القنوات مفيدة لأنها تجعل تمرير العمل صريحًا:
- من ينتج العمل
- من يستهلكه
- أين ينبغي أن يتوقف النظام
غالبًا ما يكون ذلك أكثر وضوحًا وأمانًا من ترك التزامن يتوسع بشكل ضمني.
القاعدة الأفضل
لا تسأل "هل يمكن أن يكون هذا متزامنًا؟"
اسأل:
- ما هو الاختناق؟
- كم من التزامن يمكن أن يتحمله؟
- أين يجب أن يحدث الضغط الخلفي؟
هذه هي الطريقة التي يصبح بها التزامن في Go هندسة بدلاً من التفاؤل.
قراءة إضافية