Heim  >  Artikel  >  Backend-Entwicklung  >  Warten Sie, bis ein Wert in einer Karte in Go verfügbar ist

Warten Sie, bis ein Wert in einer Karte in Go verfügbar ist

王林
王林nach vorne
2024-02-05 21:51:071160Durchsuche

等待映射中的值在 Go 中可用

Frageninhalt

Ich habe ein Programm, das im Wesentlichen drei Dinge tut: den Wert eines Schlüssels festlegen, den Wert abrufen, falls vorhanden, oder warten, bis der Wert für einen bestimmten Schlüssel verfügbar ist. Mein erster Gedanke: Wenn ich eine Methode mit map[string]interface{} 的新类型 - 其中存储“持久”值。除此之外,为了等待一个值,我计划使用 map[string](chan struct{})。当调用 set() erstelle, würde ich an diesen Kanal schreiben und jeder, der darauf wartet, weiß, dass der Wert da ist.

Ich kenne die Schlüssel vorher nicht – sie sind zufällig. Ich bin mir nicht sicher, wie ich die wait()-Methode richtig implementieren soll.

type Map struct {
    sync.Mutex

    m    map[string]interface{}
    wait map[string]chan (struct{})
}


func (m *Map) Set(key string, value interface{}) {
    m.ensureWaitChan(key)

    m.Lock()
    defer m.Unlock()

    m.m[key] = value

    // Signal to all waiting.
    m.wait[key] <- struct{}{}
}


func (m *Map) Wait(key string) interface{} {
    m.ensureWaitChan(key)

    m.Lock()
    
    value, ok := m.m[key]
    if ok {
        m.Unlock()
        return value
    }

    m.Unlock()
    // <------ Unlocked state where something might happen.
    <-m.wait[key]

    value := m.m[key]

    return value    
}

// If the channel does not exist for those waiting - create it.
func (m *Map) ensureWaitChan(key string) {
    m.Lock()
    defer m.Unlock()

    _, ok := m.wait[key]
    if ok {
        return
    }

    m.wait[key] = make(chan struct{}, 100)
}

Das Problem ist – es gibt eine Race-Bedingung in wait() – nachdem ich den Mutex freigegeben habe, aber bevor ich anfange, auf dem Kanal nach eingehenden Werten zu lauschen.

Was ist der beste Weg, mit diesem Problem umzugehen? Alle anderen Vorschläge, wie dies erreicht werden kann, sind willkommen. Ich bin mir sicher, dass es einen besseren Weg geben muss, dies zu erreichen. Ich frage den Wert nicht in einem festen Intervall oder ähnlichem ab.


Richtige Antwort


Was Sie suchen, ist eine Mischung aus einer Synchronisierungskarte und einem Nachrichtenbroker. Wir können dies erreichen, indem wir Kommunikations- und Synchronisierungskanäle nutzen, sodass Abonnenten die Nachricht erhalten können, sobald sie veröffentlicht wird (sofern sie sich nicht bereits im Cache befindet).

type Map struct {
    sync.Mutex

    m    map[string]any
    subs map[string][]chan any
}

func (m *Map) Set(key string, value any) {
    m.Lock()
    defer m.Unlock()

    m.m[key] = value

    // Send the new value to all waiting subscribers of the key
    for _, sub := range m.subs[key] {
        sub <- value
    }
    delete(m.subs, key)
}

func (m *Map) Wait(key string) any {
    m.Lock()
    // Unlock cannot be deferred so we can unblock Set() while waiting

    value, ok := m.m[key]
    if ok {
        m.Unlock()
        return value
    }

    // if there is no value yet, subscribe to any new values for this key
    ch := make(chan any)
    m.subs[key] = append(m.subs[key], ch)
    m.Unlock()

    return <-ch
}

Da Abonnenten den Karten-Mutex während des Wartens entsperren müssen, können sie nicht sicher auf neue Nachrichten zugreifen, die der Karte hinzugefügt wurden. Wir senden die neuen Werte direkt über ihre eigenen Kanäle an alle Abonnenten, sodass wir keine weiteren Synchronisierungen hinzufügen müssen set, um sicherzustellen, dass alle Abonnenten zufrieden sind, bevor wir die Karte selbst freischalten. Durch das frühzeitige Entsperren der Karte können Abonnenten sie direkt lesen, gleichzeitig können jedoch auch neue Werte eingefügt werden, was zu inkonsistenten Ergebnissen führt.

Laufversion, enthält auch generische map Implementierung mit Typparametern: https://go.dev/play/p/an7vrspdgmo

Das obige ist der detaillierte Inhalt vonWarten Sie, bis ein Wert in einer Karte in Go verfügbar ist. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen