Thread-Safe Stack Implementation in Go
This blog demonstrates how to implement a thread-safe stack in Go using sync.RWMutex. It ensures safe concurrent access in multi-threaded environments. Below is a detailed breakdown.
Key Concepts
- 
Stack: A data structure following the Last-In-First-Out (LIFO) principle, where items are added and removed from the top. 
- 
Thread-Safety: Ensures multiple goroutines can perform operations on shared resources (the stack) without causing data corruption. 
- 
sync.RWMutex: - Read Lock (RLock): Allows multiple readers concurrently.
- Write Lock (Lock): Ensures exclusive access for operations likePushandPop.
 
- Read Lock (
This boosts performance by allowing concurrent reads but restricting writes.
Structure and Methods
1. ItemType
Defined as an empty interface (interface{}), it allows the stack to hold any type of data (e.g., int, string, structs).
2. Stack Struct
Contains:
- items []ItemType: Holds the stack elements.
- rwLock sync.RWMutex: Manages concurrent access.
Methods
- 
New(): 
 Initializes and returns a new empty stack.
- 
Push(t ItemType): 
 Adds an item to the top:- Acquires a write lock (Lock()) to block other operations.
- Appends the item.
- Releases the lock (Unlock()).
 
- Acquires a write lock (
- 
Pop(): 
 Removes and returns the top item:- Acquires a write lock.
- Removes the last item from the slice.
- Resizes the slice.
- Releases the lock.
 
- 
Size(): 
 Returns the number of items in the stack:- Acquires a read lock (RLock()), allowing other reads simultaneously.
- Returns the size.
- Releases the lock (RUnlock()).
 
- Acquires a read lock (
- 
All(): 
 Returns all items in the stack with a read lock.
- 
IsEmpty(): 
 Checks if the stack is empty, using a read lock.
package main
import (
	"fmt"
	"sync"
)
// ItemType - The type of item in the stack.
type ItemType interface{}
// Stack - Stack of items.
type Stack struct {
	// Slice of type ItemType, it holds items in the stack.
	items []ItemType
	// rwLock for handling concurrent operations on the stack.
	rwLock sync.RWMutex
}
// New - Creates a new Stack.
func (stack *Stack) New() *Stack {
	stack.items = []ItemType{}
	return stack
}
// Push - Adds an Item to the top of the stack
func (stack *Stack) Push(t ItemType) {
	//Initialize items slice if not initialized
	if stack.items == nil {
		stack.items = []ItemType{}
	}
	// Acquire read, write lock before inserting a new item in the stack.
	stack.rwLock.Lock()
	// Performs append operation.
	stack.items = append(stack.items, t)
	// This will release read, write lock
	stack.rwLock.Unlock()
}
// Pop removes an Item from the top of the stack
func (stack *Stack) Pop() *ItemType {
	// Acquire read, write lock as items are going to modify.
	stack.rwLock.Lock()
	// Popping item from items slice.
	item := stack.items[len(stack.items)-1]
	//Adjusting the item's length accordingly
	stack.items = stack.items[0 : len(stack.items)-1]
	// Release read-write lock.
	stack.rwLock.Unlock()
	// Return last popped item
	return &item
}
// Size return size i.e. number of items present in stack.
func (stack *Stack) Size() int {
	// Acquire read lock
	stack.rwLock.RLock()
	// defer operation of unlocking.
	defer stack.rwLock.RUnlock()
	// Return length of items slice.
	return len(stack.items)
}
// All - return all items present in stack
func (stack *Stack) All() []ItemType {
	// Acquire read lock
	stack.rwLock.RLock()
	// defer operation of unlocking.
	defer stack.rwLock.RUnlock()
	// Return items slice to the caller.
	return stack.items
}
// IsEmpty - Check is stack is empty or not.
func (stack *Stack) IsEmpty() bool {
	// Acquire read lock
	stack.rwLock.RLock()
	// defer operation of unlock.
	defer stack.rwLock.RUnlock()
	return len(stack.items) == 0
}
func main() {
	stack := Stack{}
	fmt.Println(stack.All())
	stack.Push(10)
	fmt.Println(stack.All())
	stack.Push(20)
	fmt.Println(stack.All())
	stack.Push(30)
	fmt.Println(stack.All())
	stack.Push(40)
	fmt.Println(stack.All())
	stack.Push(50)
	fmt.Println(stack.All())
	stack.Push(60)
	fmt.Println(stack.All())
	stack.Pop()
	fmt.Println(stack.All())
	stack.Pop()
	fmt.Println(stack.All())
	stack.Pop()
	fmt.Println(stack.All())
	stack.Pop()
	fmt.Println(stack.All())
	stack.Pop()
	fmt.Println(stack.All())
	stack.Pop()
	fmt.Println(stack.All())
}
Conclusion
The thread-safe stack in Go demonstrates the power of sync.RWMutex for managing shared resources. Its methods ensure safe concurrent reads and writes, making it a robust solution for handling stacks in multi-threaded programs.
This implementation is essential knowledge for anyone working with concurrent programming in Go.