2016-06-23 1 views
0

저는 가능한 많은 수의 맵 엔트리를 범위를 지정하여 채널로 보내야하는 시나리오가 있습니다. 채널의 다른 끝에서의 작업은 오랜 시간이 걸릴 수 있으며지도는 동시에 액세스됩니다 (RWMutex으로 보호). 지도는 다소 크기가 커서 임시 사본을 만들지 않으려합니다. 동시에지도 엔트리를 채널로 읽어들입니다.

type Example struct { 
    sync.RWMutex 
    m map[string]struct{} 
} 

지금 나는 이런 식으로 뭔가를 내놓았다 :

내가이 같은 구조체가 있다고 가정

:

func (e *Example) StreamAll() <-chan string { 
    toReturn := make(chan string) 
    go func() { 
     e.RLock() 
     defer e.RUnlock() 
     for k := range e.m { 
      e.RUnlock() 
      toReturn <- k 
      e.RLock() 
     } 
     close(toReturn) 
    }() 
    return toReturn 
} 

language specification는지도를 통해 이르기까지에 대한 흥미로운 비트가 있습니다

아직 도달하지 않은 맵 항목이 반복 중에 제거되면 해당 반복 값은 제거되지 않습니다. 생산 될 수있다. 반복되는 동안 맵 항목이 작성되면 해당 항목이 반복 중에 생성되거나 건너 뛸 수 있습니다.

자, 내가 알고 싶은 것은 이것입니다 : 반복마다지도가 변경 되더라도지도 위에 올리는 방법이 작동한다는 보장이 있습니까? 마지막으로 읽은 키가 삭제 된 경우 포함? 나는 모든지도 엔트리가 필요하지 않지만 그 중 대부분은 필요합니다. 당신이 키를 생성으로지도가 변화하고, 당신이 얻을 수 있도록 : 이것은 당신이 무엇을

0) 일관성 스냅 샷 :

package main 

import (
    "fmt" 
    "sync" 
) 

type Example struct { 
    sync.RWMutex 
    m map[string]struct{} 
} 

func NewExample() *Example { 
    return &Example{ 
     m: make(map[string]struct{}), 
    } 
} 

func (e *Example) Put(s string) { 
    e.Lock() 
    defer e.Unlock() 
    e.m[s] = struct{}{} 
} 

func (e *Example) Delete(s string) { 
    e.Lock() 
    defer e.Unlock() 
    delete(e.m, s) 
} 

func (e *Example) StreamAll() <-chan string { 
    toReturn := make(chan string) 
    go func() { 
     e.RLock() 
     defer e.RUnlock() 
     for k := range e.m { 
      e.RUnlock() 
      toReturn <- k 
      e.RLock() 
     } 
     close(toReturn) 
    }() 
    return toReturn 
} 

func main() { 
    e := NewExample() 
    e.Put("a") 
    e.Put("b") 

    values := e.StreamAll() 
    // Assume other goroutines concurrently call Put and Delete on e 
    for k := range values { 
     fmt.Println(k) 
    } 
} 
+0

놀이터에 대한 링크가 작동하지 않습니다. – OneOfOne

+0

감사합니다. 지금 질문에 답했습니다. – mrd0ll4r

+0

'range' 액션 자체는 동시에 안전하게 수행되어야하며 맵이 변경되면 이상한 요소 시퀀스가 ​​생길 수 있습니다 (맵의 범위를 지정할 때마다 얻을 수있는 요소의 순서는 –

답변

1

내가 3 선택을 참조하십시오 여기에

는 완벽한 예입니다 당신이 얻는 것. 나는 당신의 잠금이 정확하다는 것을 완전히 확신하지 못합니다. 그것은 나에게 정말로 의심스러운 것처럼 보인다. 물론 경주 감지기로 광범위하게 테스트하십시오.

1) "Stop The World" - 모든 키를 생성하는 동안지도에 대한 쓰기 권한을 차단할 수 있습니다. 키를 생성하는 것은 항목을 처리하는 것보다 훨씬 빠르며 처리 할 항목의 완벽한 일관성있는 스냅 샷을 제공합니다. 불행하게도, 공동 루틴에 키를 보낼 때 처리 할 때까지 키가 존재하지 않을 수 있습니다. 당신이 그걸로 괜찮은 것 같네요. 모든 키의 사본을 저장해야하므로 잘하면이 키는 괜찮습니다.

2) MVCC (다중 버전 동시성 제어) 롤 - 1 개의 맵 대신 2를 사용하십시오. A와 B라고합시다. 두 번째 맵, 그 다음 역할 전환.

  • RW 잠금 옆에 부울을 추가하여지도를 보호하십시오.
  • 정상적으로 RW 잠금 장치를 꺼내지 만 양측지도를 참조하십시오.
  • 부울 값이 참일 때 A에서 읽기/쓰기가 가능하지만 A에서 찾을 수없는 경우 B로 읽는 것을 허용합니다.
  • 부울 값이 거짓 일 때 B에서 읽기/쓰기가 가능하지만 읽기는 허용됩니다. B에서 찾을 수없는 경우 A로 "폴백"합니다.

백그라운드 작업을 시작할 때 부울을 반전하는 동안 RW 잠금 장치를 꺼내십시오.이제 각각의 "gouroutine"을 호출하여 "폴백 (fall back)"맵의 키를 반복 할 수 있습니다. 아무도 그 맵에 쓰지 않기 때문에 키가 존재한다는 것이 보장됩니다.

goroutine의 B에서 삭제할 수 있습니다 (읽는 동안 잠금을 사용하고 삭제하는 동안 다시 사용해야 함).

하지만 더 좋을 수도/단지 (모든 것이 읽기 때문에), 다음 다음 "B를 수행하여 B를 닦아 모든 할 수있는 goroutines가 대기 NO 잠금 모든 항목을 처리 할 간단한 = 만들기() "를 사용하여 빈지도를 가져옵니다. 이렇게하면 한 번에 모든 메모리를 비우고 삭제 후에도 수행해야하는 회계를 절약 할 수 있습니다.

지도를 지우거나 (모든 항목을 삭제 한 경우) 다른 방법으로 부울을 뒤집은 다음 RW 맵을 처리 한 다음 다른지도 처리를 시작할 수 있습니다.

항목을 자주 업데이트하면지도가 2 부로 나뉘어집니다. 이 경우 : 1) WRITES가 대체 맵을 확인하도록하십시오. 그곳에 있다면, 그것을 업데이트하십시오. 그렇지 않으면, 메인 맵을 업데이트하십시오. 2) 백그라운드 처리 전에 맵에서 항목을 삭제하십시오. (대량 삭제 트릭을 사용할 수 없습니다.)

+0

이 답변을 주셔서 감사합니다. 아래에 댓글을 달았습니다. OP, 나는 골란 누에 이것을 게시했다. 잠시 후 https://groups.google.com/forum/#!topic/Golang-nuts/vkysJuKen1A 스레드입니다. 그들은 당신과 비슷한 점을 만들고, 실제로 얼랭 (Erlang) 솔루션이나 2에서 설명한 것과 같은 것을해야 할 수도 있습니다. 지금 당장은 내가 사용했던 잠금 장치가 실제로 작동하는지 아닌지를 알고 싶지만 프로젝트가 진행되지 않고 있습니다. – mrd0ll4r

관련 문제