2016-07-09 3 views
2

Ich versuche zu Lernzwecken ein einfaches Server/Client-Chat-Programm zu schreiben, aber ich bin stecken. Ich möchte die Leave Funktion entfernen Sie den Zeiger, der es passiert, und aktualisieren Sie die Scheibe in der Struktur, so dass der Zeiger nicht mehr da ist. Aber es funktioniert nicht.Go: Wie entferne ich ein Element aus einer Scheibe und ändern Sie es in Erinnerung

Beispiel: Input, Output

type Room struct { 
    Name  string 
    Visitors []*net.Conn 
} 


func (r *Room) Leave(pc *net.Conn) { 
    for i, pv := range r.Visitors { 
     //found the connection we want to remove 
     if pc == pv { 
      fmt.Printf("Before %v\n",r.Visitors) 
      r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...) 
      fmt.Printf("Before %v\n",r.Visitors)     
      return 
     } 
    } 
} 
+0

Das Entfernen Logik korrekt ist (die Ausgabe bestätigt auch diese). Das Problem könnte das Fehlen oder die falsche Synchronisierung zwischen mehreren goroutines sein (Ihre Frage sagt nichts darüber aus), oder es gibt andere Probleme in anderen Teilen Ihres Codes, die Sie nicht veröffentlicht haben. – icza

+0

Es ist wahr, dass es nicht gemacht Anstrengung, um sicherzustellen, das mit mehreren goroutines funktioniert, aber ich dachte, sie es mit einem einzigen sicherzustellen Arbeit und Parallelität Überprüfung später hinzufügen, wenn es für einen Thread funktioniert (vielleicht ist dies eine schlechte Haltung) . Ich habe den ganzen Code in einem [gist] (https: //gist.github.com/Oliv95/f79c97cc7d0cba8a1fa3536a5e3caa7a) Wenn Sie die Zeit haben, zu suchen –

Antwort

0

Sie verwenden einen Zeiger auf eine Schnittstelle (Visitors []*net.Conn) in Ihrem Room Typ. Sie sollten niemals einen Zeiger auf einen Schnittstellenwert benötigen. Eine Schnittstelle ist value ist wie ein generischer Content-Holder, und ihre Darstellung im Speicher ist anders als eine Struktur, die diese Schnittstelle implementiert.

Sie sollten einfach den Visitors Typ verwenden deklarieren eine Schnittstelle zu sein:

type Room struct { 
    Name  string 
    Visitors []net.Conn // should not be a pointer to interface value 
} 


func (r *Room) Leave(pc net.Conn) { // same thing as above 
    for i, pv := range r.Visitors { 
     // found the connection we want to remove 
     // in the comparison below actual concrete types are being compared. 
     // both these concrete types must be comparable (they can't be slices for example 
     if pc == pv { 
      r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...) 
      return 
     } 
    } 
} 

Beachten Sie, dass der Vergleich oben (pc == pv) nicht so trivial ist. Lesen Sie es hier: https://golang.org/ref/spec#Comparison_operators

Beziehen Sie sich auch auf diese Frage: Why can't I assign a *Struct to an *Interface?

+0

Das Problem besteht auch nach der vorgeschlagenen Änderung noch. Ich habe eine ganze Menge Code erstellt, vielleicht liegt die Ursache des Fehlers irgendwo anders. https://gist.github.com/Oliv95/f79c97cc7d0cba8a1fa3536a5e3caa7a –

+0

Werden die 'Before remove',' After remove' Nachrichten gedruckt? – abhink

+0

nvm sah nur die Ausgabe .. – abhink

2

Das Entfernen Logik korrekt ist (die Ausgabe auch dies bestätigt). Das Problem ist der Mangel an Synchronisation zwischen mehreren goroutines (Ihre Frage sagt nichts darüber).

Du hat gesagt (in Ihrem Kommentar), dass Sie diese zuerst mit 1 goroutine erhalten arbeiten wollen, und später mit Synchronisation befassen. Aber Ihr Code verwendet bereits mehrere goroutines, so können Sie diesen Luxus nicht haben kann:

//Let a goroutine Handle the connection 
go handleConnection(conn) 

Mehrere goroutines sind Lesen und Schreiben der Room.Visitors Scheibe, so haben Sie keine andere Wahl, als den Zugriff darauf zu synchronisieren.

wäre ein Beispiel sync.RWLock zu verwenden:

utils.go:

type Room struct { 
    Name  string 
    Visitors []net.Conn 
    mux  sync.RWLock 
} 


func (r *Room) Leave(c net.Conn) { 
    r.mux.Lock() 
    defer r.mux.Unlock() 
    for i, v := range r.Visitors { 
     //found the connection we want to remove 
     if c == v { 
      fmt.Printf("Before remove %v\n", r.Visitors) 
      r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...) 
      fmt.Printf("After remove %v\n", r.Visitors) 
      return 
     } 
    } 
} 

, wenn auch andere Code Room.Visitors berührt, auch den Code sperren (Sie RWMutex.RLock() verwenden können, wenn Sie es nur gerade lesen) .

Ebenso müssen Sie den Zugriff auf alle Variablen synchronisieren, die von mehreren goroutines gelesen/geändert werden.

berücksichtigen auch das Element Nullung, die nach der Entfernung befreit ist, sonst die darunter liegende Array wird noch diesen Wert halten, den gargabe Kollektor verhinderte freie Speicher, um richtig durch sie verwendet. Weitere Informationen zu diesem Thema finden Does go garbage collect parts of slices?

+0

Ich habe die Mutex-Sperre für die verlassene Funktion hinzugefügt und ich gebe dem Element nil, bevor ich es entfernen (ich denke, das ist, was Sie durch Nullsetzen des Elements, das befreit wird), aber das Problem ist immer noch da, ich habe jetzt nur noch ein paar nil elements in meinem slice. [Beispiel] (http://imgur.com/a/X98oo) r.Visiting [i] = null r.Visiting = append (r.Visiting [: i], r.Visiting ([i + 1: ] ...) –

+0

@AxelOlivecrona Nein Der freie Platz nach dem Entfernen eines Elements ist das letzte Element (vor dem Entfernen), also zum Nullstellen, speichern Sie den Scheibenwert (die Überschrift), entfernen Sie das Element und setzen Sie den letzten Wert auf Null in der gespeicherten Scheibe (zuteilen Sie 'nil' im Falle von Schnittstellen). Sie zerlegen nicht das letzte Element, nur dasjenige, das entfernt wird (und bald überschrieben wird), also hat es keine wirkliche Wirkung (außer das entfernbare ist das letzte Element)) – icza

+0

@AxelOlivecrona Bitte stellen Sie sicher, dass Sie alle anderen freigegebenen Variablenzugriffe synchronisieren, nicht nur 'Room.Visitors', das war nur ein Beispiel. – icza

Verwandte Themen