Sling Academy
Home/Golang/Creating Thread-Safe Function Wrappers in Go

Creating Thread-Safe Function Wrappers in Go

Last updated: November 26, 2024

Concurrency is a core feature of the Go programming language, making it an attractive option for developers who need to build efficient and resilient software. One key aspect of writing correct concurrent code is ensuring that sharing data between goroutines is done safely. In this article, we’ll explore how to create thread-safe function wrappers in Go.

Understanding Thread Safety

Thread safety means ensuring that shared data is accessed only in intended ways when being accessed by multiple threads (or goroutines, in the case of Go). The absence of proper thread safety mechanisms can lead to race conditions, where the outcome of operations depend on the non-deterministic timing of goroutines.

Using Mutex for Thread Safety

Go provides synchronization primitives such as mutexes to help manage concurrent access to shared resources. A sync.Mutex is used to ensure that only one goroutine can access a critical section of code at a time.

Here’s an example of using a sync.Mutex to create a thread-safe function wrapper around incrementing a counter:


package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    mu sync.Mutex
    count int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

func main() {
    counter := Counter{}
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.Increment()
        }()
    }

    wg.Wait()
    fmt.Println("Final Counter:", counter.Value())
}

Using Function Wrappers

We can take this concept a step further by implementing a wrapper that makes any function thread-safe. This can be particularly useful if you have many operations that need the same form of synchronization.


package main

import (
    "fmt"
    "sync"
)

type SafeFunction struct {
    mu sync.Mutex
}

func (s *SafeFunction) Run(fn func()) {
    s.mu.Lock()
    defer s.mu.Unlock()
    fn()
}

func main() {
    safeFunc := SafeFunction{}
    var wg sync.WaitGroup
    counter := 0

    increment := func() {
        counter++
    }

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            safeFunc.Run(increment)
        }()
    }

    wg.Wait()
    fmt.Println("Final counter value:", counter)
}

Conclusion

Using Go's synchronization primitives like sync.Mutex together with function wrappers can make handling concurrency more straightforward and robust. Thread-safe function wrappers abstract the complexity of locking mechanisms and help in maintaining cleaner and safer concurrent code.

Next Article: Writing Functional Tests with Dependency Injection in Go Functions

Previous Article: Exploring Recursive Variadic Functions for Complex Arguments in Go

Series: Functions in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant