golang 에서는 반복문을 이용하여 array, slice, map 을 손쉽게 iteration을 수행할 수 있도록 for range을 제공하고 있다.
for fruitsName, price := range fruitsPrice {
fmt.Println("과일 이름:", fruitsName, "-> 가격", price)
}
하지만 Thread Safety 한 sync.Map 은 for range를 이용하여 Iteration을 수행할 수 없고, sync.Map 에 구현되어있는 Range를 구현하여 Iteration을 수행할 수 있다.
func (m *Map) Range(f func(key, value any) bool)
sync.Map 의 Range에서 제공하는 key, value 은 모두 any 타입 입니다.
any는 interface{} 와 동일하며, 구조체 또는 golang Types에서 제공하는 모든 타입으로 변환이 가능하다.
type any = interface{}
따라서 key, value를 특정 타입으로 변환하기 위해서는 type assertion을 수행해야한다.
type conversion과 assertion의 차이는 이전에 작성한 블로그를 참조하기 바란다.
만약 위에서 정의한 fruitsPrice가 sync.Map으로 구성되어있다면 아래와 같이 Range()를 구현하여 Iteration을 수행하면 된다.
fruitsPrice.Range(func(k, v any) bool {
name := k.(string)
price := v.(uint64)
fmt.Printf("과일 이름: %s, 가격: %d\n", name, price)
return true
}
Range() 를 구현할 때 return 값으로 true/false를 이용하여 iteration을 지속할 것인지 중단할 것인지 결정하면 된다.
만약 iteration을 지속적으로 수행해야하는 경우 true를, 멈추기 위해서는 false를 return 하면 된다.
Range()의 가장 큰 장점으로는 Range를 수행하는 중에 Element가 삭제되거나 추가된 결과가 그대로 반영된다는 것이다.
물론 이미 Range를 수행한 Element가 지워진 경우 이미 수행한 Range 동작 결과가 변하는 것은 아니지만 아직 Visited 되지 않은 경우 중간에 변경된 결과가 그대로 반영된다.
아래 예제 코드를 보면 Range 를 수행하는 도중 Delete가 이루어졌고, 해당 Element는 Range에서 접근하기 전이였다면 그대로 Delete가 반영되어 Range 내에서는 Visited 되지 않는 것을 확인할 수 있다.
Range 순회 중 Delete 수행 예제 코드:
fruitsPrice := sync.Map{}
wg := sync.WaitGroup{}
fruitsPrice.Store("apple", 1000)
fruitsPrice.Store("banana", 500)
fruitsPrice.Store("orange", 2000)
fruitsPrice.Store("grape", 3000)
fruitsPrice.Store("kiwi", 1500)
fruitsPrice.Store("mango", 2500)
fruitsPrice.Store("melon", 3500)
fruitsPrice.Store("peach", 4000)
fruitsPrice.Store("pear", 4500)
fruitsPrice.Store("plum", 5000)
wg.Add(1)
go func() {
defer wg.Done()
fruitsPrice.Range(func(k, v any) bool {
name := k.(string)
price := v.(int)
fmt.Printf("fruit name: %s, price: %d\n", name, price)
return true
})
}()
wg.Add(1)
go func() {
defer wg.Done()
if price, ok := fruitsPrice.LoadAndDelete("mango"); ok {
fmt.Printf("delete mango price: %d\n", price)
}
}()
wg.Add(1)
go func() {
defer wg.Done()
if price, ok := fruitsPrice.LoadAndDelete("pear"); ok {
fmt.Printf("delete pear price: %d\n", price)
}
}()
wg.Wait()
Range 순회 중 Delete 한 결과 출력:
delete mango price: 2500
fruit name: orange, price: 2000
fruit name: melon, price: 3500
fruit name: plum, price: 5000
fruit name: banana, price: 500
delete pear price: 4500
fruit name: grape, price: 3000
fruit name: kiwi, price: 1500
fruit name: peach, price: 4000
fruit name: apple, price: 1000
Range 중 Insert, Delete가 자유롭다는 장점도 있지만 반대로 Lock이 되지 않는다는 것으로 이용 시 주의해야 한다.
2023.12.24 - [golang (go)] - golang map
2024.01.14 - [golang (go)] - golang slice elements copy (copy(), append(), for ... range)
2023.02.03 - [golang (go)] - golang type conversion [string(val)], type assertion [val.(string)]
'golang (go)' 카테고리의 다른 글
golang slice elements copy (copy(), append(), for ... range) (1) | 2024.01.14 |
---|---|
golang map (0) | 2023.12.24 |
golang - const와 iota로 enum(열거)형 구현하기 (1) | 2023.11.18 |
golang byte slice (array) compare (0) | 2023.02.07 |
golang type conversion [string(val)], type assertion [val.(string)] (0) | 2023.02.03 |
댓글