How To Synchronize threads In Go.

Single-threaded code already brings headaches. Add a second thread, it’s a graduation from a basic headache.

The fix? Mutexes: traffic cops for your threads and data.
Once you understand them, thread sync becomes second nature, language agnostic.

Wor…


This content originally appeared on DEV Community and was authored by SK

Single-threaded code already brings headaches. Add a second thread, it's a graduation from a basic headache.

The fix? Mutexes: traffic cops for your threads and data.
Once you understand them, thread sync becomes second nature, language agnostic.

Working in both C++ and Go, I’ve run into all the usual chaos:

  • Race conditions that sometimes swallow data
  • Segfaults from threads trampling memory
  • And the silent killer: deadlocks

That last one’s the worst, no crash, no error. Just a dead program, stuck in an eternal thread standoff.

But it all starts to click when you get the core idea behind a mutex.

The best part? Every language speaks mutex:

  • Go → sync.Mutex
  • C++ → std::mutex
  • Python → threading.Lock()
  • Java → ReentrantLock

In this post, I’ll break down mutexes as a concept, show you how deadlocks happen, and leave you with enough intuition to handle threaded code in any language.

Learn once → apply everywhere.

Mutexes: Mutual Exclusion Lock

Threads introduce a whole new category of problems, especially in Go, where spawning thousands is practically free.

Now imagine two threads hitting the same data source at the exact same time. That’s chaos. Race conditions, data corruption, mystery bugs, things you don’t want to debug, let alone explain to your team.

Enter mutexes: the traffic cops between your threads and shared data.

Without a lock:

thread A  --->     data source  <--- thread B

With a lock (shared between both threads):

thread A  [lock]--->     data source  <---[lock] thread B

The mutex’s job is simple: only one thread enters at a time.
If thread A owns the lock, thread B gets told: "Wait your turn."

Here’s a simple example of slice access with and without locks:

Without locks:

package main

import (
    "fmt"
    "time"
)

func main() {
    var numbers []int

    // Spin up 5 goroutines that all append to the same slice.
    for i := 0; i < 5; i++ {
        go func(n int) {
            // No locking here,this will likely cause a data race
            numbers = append(numbers, n)
            fmt.Println("Appended", n, "→", numbers)
        }(i)
    }

    // Give them a moment to run
    time.Sleep(1 * time.Second)
}

With locks:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var (
        numbers []int
        mu      sync.Mutex
    )

    for i := 0; i < 5; i++ {
        go func(n int) {
            mu.Lock()           // acquire the lock
            defer mu.Unlock()   // ensure it’s released, even on panic

            numbers = append(numbers, n)
            fmt.Println("Appended", n, "→", numbers)
        }(i)
    }

    time.Sleep(1 * time.Second)
}

Notice how we do:

mu.Lock()
defer mu.Unlock()

The defer guarantees that no matter how we exit that goroutine, normal return or panic, the lock will be released.

Once a goroutine touches shared data, lock it down. Trust me, future you will be grateful.

So, what exactly is a deadlock?

Deadlocks

Back to our traffic cop analogy:

thread A  [lock]--->     data source  <---[lock] thread B

This works because one shared lock controls access. But what happens when we introduce two shared locks in the same lane?

thread A  [lock]--[lock]->     data source  <---[lock] thread B

Now you’ve got two traffic cops, and neither knows who’s in charge. Thread A gets stuck waiting on both, forever ping-ponging in confusion. That’s a classic deadlock.

The usual suspect? Same Nested locks, calling a function that acquires a lock from within another function that’s already holding it.

Here’s a real-world example:

func (m *ScheduledTask) Create(...) (task, error) {
    m.mu.Lock()                // LOCK 1
    defer m.mu.Unlock()        // UNLOCK 1 at the end

    // ... setup task ...

    if err := m.saveTasks(); err != nil {  // LOCK 2 inside
        return task{}, err
    }

    return t, nil
}

Now look inside saveTasks:

func (m *ScheduledTask) saveTasks() error {
    m.mu.Lock()               // LOCK 2 (again)
    defer m.mu.Unlock()

    data, err := json.MarshalIndent(m.tasks, "", "  ")
    if err != nil {
        return err
    }

    return os.WriteFile(tasks, data, 0644)
}

Deadlock.
Why? Because Create() already holds the lock, and saveTasks() tries to acquire it again, before the first one is released. Go routines don’t complain, they just silently freeze. No crash, no stack trace, just a zombie thread eating resources.

And the main thread? Blissfully unaware. Keeps running while your program hangs in limbo.

If you’re serious about building real-world software, you need to understand synchronization.

The concepts apply across languages. Here's the C++ version:

std::lock_guard<std::mutex> lk(globalIPCData.mapMutex);  // locking before access
UIelement& u = uiSet.get(entityId);

Learn this well.

Once you see mutexes as traffic cops with absolute authority, most thread issues just vanish.

I’ll be posting more deep dives on backend topics,JavaScript, Golang, C++, and low-level systems on Substack. Would love to have you there; come say hi:

Coffee & Kernels

More Content:

Guide To Deep Learning

The Hitchhiker's Guide to Deep Learning: Python and JS examples.

Deep Learning : Pytorch and Tensorflow.js Examples.

favicon skdev.substack.com

How To Suck Less At Databases

How To Suck Less At Databases and Data Systems with JavaScript Examples.

Databases, Data Systems and The Language of Intent.

favicon skdev.substack.com


This content originally appeared on DEV Community and was authored by SK


Print Share Comment Cite Upload Translate Updates
APA

SK | Sciencx (2025-07-08T11:00:00+00:00) How To Synchronize threads In Go.. Retrieved from https://www.scien.cx/2025/07/08/how-to-synchronize-threads-in-go/

MLA
" » How To Synchronize threads In Go.." SK | Sciencx - Tuesday July 8, 2025, https://www.scien.cx/2025/07/08/how-to-synchronize-threads-in-go/
HARVARD
SK | Sciencx Tuesday July 8, 2025 » How To Synchronize threads In Go.., viewed ,<https://www.scien.cx/2025/07/08/how-to-synchronize-threads-in-go/>
VANCOUVER
SK | Sciencx - » How To Synchronize threads In Go.. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/07/08/how-to-synchronize-threads-in-go/
CHICAGO
" » How To Synchronize threads In Go.." SK | Sciencx - Accessed . https://www.scien.cx/2025/07/08/how-to-synchronize-threads-in-go/
IEEE
" » How To Synchronize threads In Go.." SK | Sciencx [Online]. Available: https://www.scien.cx/2025/07/08/how-to-synchronize-threads-in-go/. [Accessed: ]
rf:citation
» How To Synchronize threads In Go. | SK | Sciencx | https://www.scien.cx/2025/07/08/how-to-synchronize-threads-in-go/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.