Go: تحليل أداء Go

آخر تحديث: 2026-08-26

تحليل الأداء ليس بالأمر الغامض — فمن خلال استخدام كل من pprof و benchmark و trace معًا، يمكنك تحديد الاختناقات في وحدة المعالجة المركزية (CPU) والذاكرة والتزامن في برامج Go الخاصة بك.

عندما يرتفع وقت استجابة واجهة برمجة تطبيقات Go من 50 مللي ثانية إلى 5 ثوانٍ، هل تضيف سجلات إلى كودك لتشخيص المشكلة، أم تستخدم أدوات لتحديد مصدرها بدقة؟ في هذا الدرس، ستتقن مجموعة الأدوات الكاملة لتحليل أداء لغة Go.

1. ستتعلم



2. قصة حقيقية لمهندس برمجيات الخلفية

(1) المشكلة: ارتفع وقت استجابة واجهة برمجة التطبيقات (API) من 50 مللي ثانية إلى 5 ثوانٍ؛ واستغرق الأمر أسبوعًا من تسجيل البيانات لتحديد السبب

بوب هو مهندس «الخلفية» في فريق المدفوعات، وقد أصبحت واجهة برمجة التطبيقات (API) الخاصة به أبطأ فأبطأ:

"كانت واجهة الدفع تعمل بشكل جيد قبل شهر، لكنها تستغرق هذه الأسبوع 5 ثوانٍ للاستجابة. أضفت 50 سطراً من سجلات التشغيل، مستخدماً fmt.Println لتسجيل الطابع الزمني لبداية ونهاية كل دالة — قمت بتعديل الكود 10 مرات ونشرته 10 مرات، لكنني ما زلت غير قادر على تحديد المشكلة. سألني مديري: «لقد مر أسبوع — ما هي المشكلة بالضبط؟»"

تشير شكوكه إلى:

TEXT 📖 للعرض فقط
❌ Database too slow? — But queries only take 2ms
❌ Downstream service timeout? — Called it and the response is normal
❌ Network latency? — All in the same datacenter
✅ Actual cause: string concatenation causing massive memory allocation + frequent GC

(2) حل Go: pprof للتصحيح الدقيق

GO
import (
    "net/http"
    _ "net/http/pprof"  // One line to enable pprof
)

func main() {
    // pprof endpoints auto-registered at /debug/pprof/
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()
    // Business code continues running...
}

ثم انطلق بوب راكضًا:

BASH
# Collect 30-second CPU profile
go tool pprof -http=:8081 http://localhost:6060/debug/pprof/profile?seconds=30

# Result: flame graph shows strings.Builder only uses 2% CPU,
# while strings.Join + garbage collection uses 78% CPU!

(3) الفوائد: التخمين مقابل الأدوات

الطريقة الوقت الدقة
fmt.Println التسجيل أسبوع واحد (عمليات نشر متعددة) ❌ تخمين
تحليل أداء وحدة المعالجة المركزية باستخدام pprof 30 ثانية ✅ تحديد دقيق للوظائف الأكثر استهلاكًا للموارد
ملف تعريف الذاكرة المؤقتة باستخدام pprof 1 ثانية ✅ تفاصيل تخصيص الذاكرة حتى أرقام الأسطر


3. كيفية تشغيل pprof

▶ مثال: طريقة HTTP (الأكثر شيوعًا)

GO
package main

import (
    "fmt"
    "log"
    "net/http"
    _ "net/http/pprof"  // Import to auto-register pprof endpoints
    "time"
)

func slowFunction() {
    // Simulate a slow function
    var result string
    for i := 0; i < 100000; i++ {
        result += fmt.Sprintf("%d ", i) // Bad string concatenation
    }
}

func main() {
    // Start pprof HTTP service (separate port, not exposed externally)
    go func() {
        log.Println("pprof listening on :6060")
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()

    // Business service
    http.HandleFunc("/work", func(w http.ResponseWriter, r *http.Request) {
        slowFunction()
        fmt.Fprintln(w, "done")
    })

    log.Println("Business service listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
▶ جرّب الكود
BASH
# pprof endpoints:
# http://localhost:6060/debug/pprof/          — index
# http://localhost:6060/debug/pprof/profile   — CPU profile (default 30s)
# http://localhost:6060/debug/pprof/heap      — Heap profile
# http://localhost:6060/debug/pprof/goroutine — goroutine info
# http://localhost:6060/debug/pprof/block     — block analysis
# http://localhost:6060/debug/pprof/mutex     — lock contention analysis

▶ مثال: طرق الاختبار (benchmark + pprof)

GO
// string_bench_test.go
package main

import (
    "strings"
    "testing"
)

// Bad approach: + concatenation
func BenchmarkStringPlus(b *testing.B) {
    for i := 0; i < b.N; i++ {
        var s string
        for j := 0; j < 1000; j++ {
            s += "a"
        }
    }
}

// Good approach: strings.Builder
func BenchmarkStringBuilder(b *testing.B) {
    for i := 0; i < b.N; i++ {
        var sb strings.Builder
        for j := 0; j < 1000; j++ {
            sb.WriteString("a")
        }
        _ = sb.String()
    }
}

// Good approach: pre-allocate
func BenchmarkStringBuilderPrealloc(b *testing.B) {
    for i := 0; i < b.N; i++ {
        var sb strings.Builder
        sb.Grow(1000)
        for j := 0; j < 1000; j++ {
            sb.WriteString("a")
        }
        _ = sb.String()
    }
}
▶ جرّب الكود
BASH
# Run benchmark (view memory allocations)
$ go test -bench=. -benchmem -count=3

# Generate CPU profile
$ go test -bench=. -cpuprofile=cpu.prof -memprofile=mem.prof

# Analyze profile
$ go tool pprof -http=:8081 cpu.prof


4. ملف تعريف وحدة المعالجة المركزية

▶ مثال: تحديد نقاط الضغط في وحدة المعالجة المركزية (CPU)

GO
package main

import (
    "log"
    "net/http"
    _ "net/http/pprof"
    "strings"
)

func heavyCPU() string {
    var sb strings.Builder
    for i := 0; i < 10000; i++ {
        sb.WriteString("hello")
        sb.WriteString(" ")
        sb.WriteString("world")
        sb.WriteString("\n")
    }
    return sb.String()
}

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()

    http.HandleFunc("/cpu", func(w http.ResponseWriter, r *http.Request) {
        result := heavyCPU()
        w.Write([]byte(result[:100]))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}
▶ جرّب الكود
BASH
# Collect CPU profile (access /cpu endpoint multiple times within 30 seconds)
$ go tool pprof -http=:8081 http://localhost:6060/debug/pprof/profile?seconds=30

# CLI mode
$ go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
(pprof) top       # Show Top 10 hot functions
(pprof) list main # View per-line time in the main package
(pprof) web       # Open visualization in browser

(2) تفسير مخرجات أداة pprof top

TEXT 📖 للعرض فقط
(pprof) top
Showing nodes accounting for 4.56s, 82.31% of 5.54s total
Dropped 28 nodes (cum <= 0.03s)
      flat  flat%   sum%        cum   cum%
     2.34s 42.24% 42.24%      2.34s 42.24%  runtime.memmove
     1.12s 20.22% 62.46%      1.12s 20.22%  runtime.mallocgc
     0.56s 10.11% 72.57%      0.56s 10.11%  strings.(*Builder).copy
     ...
العمود المعنى
flat الوقت الذي تستغرقه الدالة الحالية نفسها
flat% النسبة المئوية من إجمالي الوقت الذي قضاه في وضع «المستوي»
sum% النسبة المئوية التراكمية
cum الوقت الذي استغرقته الدالة الحالية وجميع الدوال الفرعية التي تستدعيها
cum% النسبة المئوية للوقت الإجمالي الذي يمثله التراكم
💡 نصيحة: الدوال التي تحتوي على قيمة flat عالية تكون «بطيئة في حد ذاتها» (نقاط ساخنة)، في حين أن الدوال التي تحتوي على قيمة cum عالية ولكن قيمة flat منخفضة تكون «بطيئة بسبب الاستدعاءات» (مشكلات إدارية). قم بتحسين الدوال ذات القيم الأعلى لـ flat أولاً — فهذا سيحقق أسرع النتائج.



5. تحليل الذاكرة (Heap Profile)

▶ مثال: تحديد تسربات الذاكرة

GO
package main

import (
    "log"
    "net/http"
    _ "net/http/pprof"
)

var leak []string  // Global variable, never garbage collected

func memoryLeak() {
    // Appends 10000 entries on each call, never clears
    for i := 0; i < 10000; i++ {
        leak = append(leak, "leaked string data")
    }
}

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()

    http.HandleFunc("/leak", func(w http.ResponseWriter, r *http.Request) {
        memoryLeak()
        w.Write([]byte("leaked"))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}
▶ جرّب الكود
BASH
# Collect heap profile (view current memory allocation)
$ go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap

# View functions with the most allocations
$ go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap
(pprof) top
(pprof) list main.memoryLeak

(2) وضع عرض الكومة

BASH
# Four viewing modes:
-inuse_space  # Currently in-use memory (default)
-inuse_objects # Currently in-use object count
-alloc_space  # Total allocated memory
-alloc_objects # Total allocated object count

# Use alloc_space to find leaks (see who allocates the most)
$ go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap
الوضع الغرض
inuse_space الاستخدام الحالي للذاكرة (النتيجة النهائية لاكتشاف التسرب)
inuse_objects عدد الكائنات الحالية (للبحث عن عدد كبير من الكائنات الصغيرة)
alloc_space إجمالي التخصيص (لتحديد السبب الجذري لتكرار عمليات جمع القمامة)
alloc_objects إجمالي عدد عمليات التخصيص (لتحديد الكائنات قصيرة العمر)


6. تحليل أداء الجوروتين

▶ مثال: الكشف عن تسرب الجوروتينات

BASH
# View goroutine count and status
$ go tool pprof http://localhost:6060/debug/pprof/goroutine

# View goroutine stack trace (text)
$ curl http://localhost:6060/debug/pprof/goroutine?debug=2
GO
package main

import (
    "fmt"
    "log"
    "net/http"
    _ "net/http/pprof"
    "time"
)

func leakyGoroutine() {
    ch := make(chan int)
    go func() {
        // This goroutine will never exit
        val := <-ch  // Blocks forever
        fmt.Println(val)
    }()
    // ch will never receive data
}

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()

    http.HandleFunc("/leak", func(w http.ResponseWriter, r *http.Request) {
        leakyGoroutine()
        w.Write([]byte("leaked"))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}
BASH
$ curl http://localhost:6060/debug/pprof/goroutine?debug=2
# The output shows each goroutine's stack trace:
# goroutine 5 [chan receive]:
# main.leakyGoroutine.func1()
#     /app/main.go:14
# If you see many [chan receive] with no corresponding sender → leak


7. المقارنة المعيارية والتتبع

▶ مثال: Benchmark + -benchmem

GO
// bench_test.go
package main

import (
    "encoding/json"
    "testing"
)

type Data struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// Benchmark: JSON serialization performance
func BenchmarkJSONMarshal(b *testing.B) {
    data := Data{ID: 1, Name: "Alice", Email: "alice@example.com"}

    for i := 0; i < b.N; i++ {
        _, err := json.Marshal(data)
        if err != nil {
            b.Fatal(err)
        }
    }
}

// Benchmark: JSON serialization + pre-allocated buffer
func BenchmarkJSONMarshalBuffer(b *testing.B) {
    data := Data{ID: 1, Name: "Alice", Email: "alice@example.com"}
    buf := make([]byte, 0, 256)

    for i := 0; i < b.N; i++ {
        buf = buf[:0]
        result, err := json.Marshal(data)
        if err != nil {
            b.Fatal(err)
        }
        buf = append(buf, result...)
    }
}
▶ جرّب الكود
BASH
$ go test -bench=. -benchmem -count=5 ./...
BenchmarkJSONMarshal-8          10000000   156.2 ns/op   48 B/op   1 allocs/op
BenchmarkJSONMarshalBuffer-8    10000000   158.1 ns/op   48 B/op   1 allocs/op

▶ مثال: التتبع

GO
package main

import (
    "fmt"
    "os"
    "runtime/trace"
    "sync"
)

func main() {
    // Create trace file
    f, _ := os.Create("trace.out")
    defer f.Close()

    // Start trace
    trace.Start(f)
    defer trace.Stop()

    // Run code under test
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            result := fibonacci(30)
            fmt.Printf("Worker %d: %d\n", id, result)
        }(i)
    }
    wg.Wait()
}

func fibonacci(n int) int {
    if n <= 1 {
        return n
    }
    return fibonacci(n-1) + fibonacci(n-2)
}
▶ جرّب الكود
BASH
# After generating the trace file, view it in a browser
$ go tool trace trace.out
# Opens the browser, showing:
# - Goroutine analysis: how long each goroutine ran
# - Scheduling latency: when goroutines were scheduled
# - Network blocking: what goroutines are waiting for
# - System calls: when GC ran


8. مثال كامل: تحديد «استجابة بطيئة تبلغ 500 مللي ثانية»

▶ مثال: عرض توضيحي كامل لعملية تصحيح الأخطاء

GO 📖 للعرض فقط
// debug_demo.go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    _ "net/http/pprof"
    "strings"
)

// ---------- Slow API ----------

type UserResponse struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
    Bio   string `json:"bio"`
}

// Bad version: string concatenation + heavy allocation
func generateUserJSON(userID int) []byte {
    var bio strings.Builder
    // Simulate generating a large amount of text
    for i := 0; i < 1000; i++ {
        bio.WriteString(fmt.Sprintf("Line %d: User data for ID %d with some additional info\n", i, userID))
    }

    resp := UserResponse{
        ID:    userID,
        Name:  fmt.Sprintf("User_%d", userID),
        Email: fmt.Sprintf("user%d@example.com", userID),
        Bio:   bio.String(),
    }

    data, _ := json.Marshal(resp)
    return data
}

// Optimized version: pre-allocation + reduced formatting
func generateUserJSONOptimized(userID int) []byte {
    // Pre-allocate buffer
    var bio strings.Builder
    bio.Grow(50000)  // Estimated size

    for i := 0; i < 1000; i++ {
        bio.WriteString("Line ")
        bio.WriteString(fmt.Sprintf("%d", i))  // Can be further optimized with strconv.Itoa
        bio.WriteString(": User data for ID ")
        bio.WriteString(fmt.Sprintf("%d", userID))
        bio.WriteString(" with some additional info\n")
    }

    resp := UserResponse{
        ID:    userID,
        Name:  "User_" + fmt.Sprintf("%d", userID),
        Email: fmt.Sprintf("user%d@example.com", userID),
        Bio:   bio.String(),
    }

    data, _ := json.Marshal(resp)
    return data
}

// ---------- Analysis workflow ----------

/*
Debugging workflow:

(1) Step 1: Start pprof HTTP server
go run main.go (automatically starts pprof on :6060)

(2) Step 2: Load test
# In another terminal, send continuous requests
while true; do curl http://localhost:8080/user/1 > /dev/null; done

(3) Step 3: Collect CPU profile
go tool pprof -http=:8081 http://localhost:6060/debug/pprof/profile?seconds=30

(4) Step 4: View flame graph in browser
- Look for the widest color blocks → hot functions
- If you see runtime.memmove / runtime.mallocgc → excessive memory allocation
- Click main.generateUserJSON → view per-line code timing

(5) Step 5: View Heap profile
go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap
(pprof) top
*/

func main() {
    // pprof
    go func() {
        log.Println("pprof on :6060")
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()

    // Business endpoint
    http.HandleFunc("/user/{id}", func(w http.ResponseWriter, r *http.Request) {
        id := r.PathValue("id")
        var userID int
        fmt.Sscanf(id, "%d", &userID)

        data := generateUserJSON(userID)
        w.Header().Set("Content-Type", "application/json")
        w.Write(data)
    })

    log.Println("Service listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
78 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)
100%
flowchart TD
    A[API slow response] --> B{Problem type?}
    B -->|High CPU| C[pprof CPU profile]
    B -->|High memory| D[pprof Heap profile]
    B -->|Many goroutines| E[pprof goroutine profile]
    B -->|Scheduling latency| F[go tool trace]

    C --> C1[View flame graph]
    C1 --> C2{Hot function?}
    C2 -->|runtime.memmove| G[Reduce memory allocation]
    C2 -->|Business function| H[Optimize algorithm / add cache]

    D --> D1[View alloc_space]
    D1 --> D2{Who allocates the most?}
    D2 -->|strings.Builder| I[Pre-allocate with Grow]
    D2 -->|Temporary objects| J[Use sync.Pool]

    E --> E1[View goroutine stack]
    E1 --> E2{Goroutine status?}
    E2 -->|chan receive blocked| K[Check channel sender]
    E2 -->|IO wait| L[Check connection pool]

    F --> F1[View goroutine analysis]
    F1 --> F2{Scheduling latency?}
    F2 -->|GC pause| M[Reduce memory allocation]
    F2 -->|System calls| N[Optimize IO operations]
💡 نصيحة: نهج من ثلاث خطوات لتحسين الأداء: القياس → التحديد → التحسين. لا تحاول تخمين أماكن وجود الاختناقات — قم بتشغيل pprof لجمع البيانات أولاً، ثم استخدم تلك البيانات لاتخاذ القرارات. أكثر اختناقات الأداء شيوعًا في لغة Go هي التخصيص المفرط للذاكرة (ارتفاع ضغط GC) والتسلسل غير الفعال للسلاسل النصية.



❓ أسئلة شائعة

س كيف يمكنني تشغيل pprof؟
ج هناك طريقتان: (1) طريقة HTTP: import _ "net/http/pprof"، ثم قم بتشغيل خدمة HTTP؛ حيث يتم تسجيل نقطة النهاية تلقائيًا في /debug/pprof/؛ (2) وضع الاختبار: go test -cpuprofile=cpu.prof -memprofile=mem.prof. في بيئة الإنتاج، استخدم طريقة HTTP عبر منفذ مخصص (غير متاح للجمهور).
س ما الفرق بين ملف تعريف وحدة المعالجة المركزية (CPU) وملف تعريف الـ«هيوب» (heap)؟
ج يقوم ملف تعريف وحدة المعالجة المركزية (CPU) بأخذ عينات من «الدوال التي تقوم وحدة المعالجة المركزية بتنفيذها حاليًا» (أخذ عينات موقوتة)، ويُستخدم لتحديد نقاط الضغط في وحدة المعالجة المركزية. أما ملف تعريف الـ«هيوب» (heap) فيقوم بأخذ عينات من «الكائنات التي تم تخصيصها في الذاكرة»، ويُستخدم لتحديد تسربات الذاكرة والضغط على آلية جمع القمامة (GC). وهذان نوعان متميزان من ملفات التعريف ويمكن جمعهما في آن واحد.
س ما الذي يجب أن أبحث عنه في ملف تعريف الجوروتين؟
ج تحقق من الأرقام والحالة الخاصة بالجوروتينات. استخدم curl /debug/pprof/goroutine?debug=2 لعرض تتبع المكدس لكل جوروتين. قد يشير وجود عدد كبير من goroutines في حالة [chan receive] إلى وجود تسرب في القناة. وقد يشير وجود عدد كبير من goroutines في حالة [IO wait] إلى عدم كفاية تجمع الاتصالات.
س كيف يمكنني تفسير نتائج خيار -benchmem في اختبار الأداء؟
ج هناك ثلاثة أعمدة: ns/op (الوقت لكل عملية)، وB/op (عدد البايتات المخصصة لكل عملية)، وallocs/op (عدد عمليات التخصيص لكل عملية). هدف التحسين: تقليل allocs/op (عدد عمليات التخصيص)، لأن وقت GC مرتبط بعدد الكائنات.
س ما الفرق بين trace و pprof؟
ج يقوم pprof بأخذ «لقطة» (عينة) للإجابة عن سؤال «أين يقع عنق الزجاجة؟»؛ بينما يسجل trace «فيديو» (دفق الأحداث) للإجابة عن سؤال «لماذا يكون الأداء بطيئًا؟». يوفر Trace تسلسلًا زمنيًا كاملاً لجدولة goroutine — متى يتم تشغيل goroutine، ومتى يتم حجبها، ومتى تتوقف عملية جمع القمامة (GC). استخدم pprof أولاً لتحديد منطقة المشكلة، ثم استخدم trace لتحليل السبب الجذري.
س كيف يمكنني استخدام كاشف التنافس؟
ج go run -race main.go أو go test -race ./.... يكتشف كاشف التنافس حالات التنافس على البيانات أثناء وقت التشغيل — حيث تؤدي عمليات القراءة والكتابة المتزامنة على نفس المتغير (مع وجود عملية كتابة واحدة على الأقل) إلى إصدار تحذير. يُنصح دائمًا بتمكين -race في CI/CD، ولكنه يؤدي إلى إبطاء التنفيذ بشكل كبير (5–20 مرة)، لذا لا تقم بتمكينه في بيئات الإنتاج.
س ما هي بعض العوائق الشائعة التي تؤثر على أداء لغة Go؟
ج (1) استخدام + لتسلسل السلاسل النصية بدلاً من strings.Builder؛ (2) نسيان تخصيص أحجام الشرائح/الخرائط مسبقًا؛ (3) التكرار المتكرر لعملية تسلسل/إلغاء تسلسل JSON؛ (4) تسربات Goroutine التي تؤدي إلى بقاء الموارد غير مُحررة؛ (5) الحجب الناتج عن الاستخدام غير السليم للقنوات؛ (6) التنازع الشديد على القفل. استخدم pprof لتحديد هذه المشكلات وتحسينها واحدة تلو الأخرى.

📖 ملخص


📝 تمارين

  1. أساسي (مستوى الصعوبة ⭐): اكتب برنامجًا يعاني من مشكلات في الأداء (يتضمن عددًا كبيرًا من عمليات ربط السلاسل النصية باستخدام +)، وقم بتفعيل نقطة نهاية HTTP لـ pprof. قم بتشغيل go tool pprof -http=:8081 لعرض ملف تعريف وحدة المعالجة المركزية (CPU) وتحديد النقاط الساخنة. ثم قم بتحسين الكود باستخدام strings.Builder وقارن بين ملفات تعريف وحدة المعالجة المركزية قبل التغيير وبعده.

  2. متقدم (صعوبة ⭐⭐): تحليل أداء تسلسل JSON باستخدام benchmark و pprof. المتطلبات: (1) إنشاء بنية (struct) تحتوي على 100 حقل؛ (2) مقارنة أداء json.Marshal و json.Encoder؛ (3) استخدم -benchmem لعرض تخصيص الذاكرة؛ (4) استخدم -cpuprofile لإنشاء ملف تعريف الأداء وتحليل النقاط الساخنة باستخدام go tool pprof.

  3. التحدي (الصعوبة ⭐⭐⭐): تشخيص وإصلاح برنامج يعاني من تسربات في الذاكرة. ستُزوَّد بجزء من كود لغة Go يحتوي على تسربات في الذاكرة (تسربات في goroutine + تسربات في الشرائح). المتطلبات: (1) استخدم أداة تحليل الكومة pprof لتحديد مصدر التسربات؛ (2) استخدم ملف تعريف goroutine للتأكد من أرقام التسربات؛ (3) استخدم -race لاكتشاف مشكلات التزامن؛ (4) بعد إصلاح جميع المشكلات، استخدم pprof للتحقق من عدم وجود تسربات أخرى؛ (5) اكتب تقريرًا تشخيصيًا كاملاً.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%