2012-09-04 3 views
7

Ich versuche, eine Warteschlange Struktur, die Push-und Pop-Funktionen haben.Wie implementiert man einen Zähler bei der Verwendung der Golou-Goroutine?

Ich brauche 10 Threads Push und weitere 10 Threads Pop-Daten, genau wie ich es im Code unten getan habe.

Fragen: 1. Ich muss ausdrucken, wie viel ich gedrückt habe, aber ich weiß nicht, wie das geht. 2. Gibt es trotzdem eine Beschleunigung meines Codes? Der Code ist zu langsam für mich.

package main 

import (
    "runtime" 
    "time" 
) 

const (
    DATA_SIZE_PER_THREAD = 10000000 
) 

type Queue struct { 
    records string 
} 


func (self Queue) push(record chan interface{}) { 
    // need push counter 
    record <- time.Now() 
} 

func (self Queue) pop(record chan interface{}) { 
    // need pop counter 
    <- record 
} 

func main() { 
    runtime.GOMAXPROCS(runtime.NumCPU()) 
    //record chan 
    record := make(chan interface{},1000000) 
    //finish flag chan 
    finish := make(chan bool) 
    queue := new(Queue) 
    for i:=0; i<10; i++ { 
     go func() { 
      for j:=0; j<DATA_SIZE_PER_THREAD; j++ { 
       queue.push(record) 
      } 
      finish<-true 
     }() 
    } 
    for i:=0; i<10; i++ { 
     go func() { 
      for j:=0; j<DATA_SIZE_PER_THREAD; j++ { 
       queue.pop(record) 
      } 
      finish<-true 
     }() 
    } 
    for i:=0; i<20; i++ { 
     <-finish 
    } 
} 

Antwort

11

Es gibt ein paar Dinge, die Sie beheben sollten.

  • Die Methoden im Queue-Typ sollten Zeigerempfänger haben. Andernfalls wird bei jedem Aufruf der Methode eine Kopie des aktuellen Queue-Typs erstellt und alle Änderungen an Queue-Feldern bleiben nicht über den Methodenaufruf hinaus bestehen.

  • Warten auf alle Routinen zu beenden, kann mit einem sync.WaitGroup erfolgen. Diese ist genau das, wofür es entwickelt wurde.

  • Die Verwaltung eines Thread-sicheren Push/Pop-Zählers innerhalb des Warteschlangentyps kann unter Verwendung des Pakets sync/atomic erfolgen.

  • So weit wie Geschwindigkeit geht, von Ihrem Beispiel, bin ich nicht ganz sicher, was Sie erreichen wollen. Irgendwelche Optimierungen könnten aufkommen, wenn Sie das ein wenig weiter ausführen. Hier

    ist ein Beispiel, das ich aus dem Code geändert:

    package main 
    
    import (
        "log" 
        "runtime" 
        "sync" 
        "sync/atomic" 
        "time" 
    ) 
    
    const SizePerThread = 10000000 
    
    type Queue struct { 
        records string 
        count int64 
    } 
    
    func (q *Queue) push(record chan interface{}) { 
        record <- time.Now() 
    
        newcount := atomic.AddInt64(&q.count, 1) 
        log.Printf("Push: %d", newcount) 
    } 
    
    func (q *Queue) pop(record chan interface{}) { 
        <-record 
    
        newcount := atomic.AddInt64(&q.count, -1) 
        log.Printf("Pop: %d", newcount) 
    } 
    
    func main() { 
        var wg sync.WaitGroup 
    
        runtime.GOMAXPROCS(runtime.NumCPU()) 
    
        record := make(chan interface{}, 1000000) 
        queue := new(Queue) 
    
        // We are launching 20 goroutines. 
        // Let the waitgroup know it should wait for as many 
        // of them to finish. 
        wg.Add(20) 
    
        for i := 0; i < 10; i++ { 
         go func() { 
          defer wg.Done() 
    
          for j := 0; j < SizePerThread; j++ { 
           queue.push(record) 
          } 
         }() 
    
         go func() { 
          defer wg.Done() 
    
          for j := 0; j < SizePerThread; j++ { 
           queue.pop(record) 
          } 
         }() 
        } 
    
        // Wait for all goroutines to finish. 
        wg.Wait() 
    } 
    
    +0

    Danke @jimt, aber ich bin nicht ganz zu verstehen, was du meinst ** der Queue-Typ sollte Zeiger Empfänger haben **, könnten Sie mir einen Link auf Golang Doc? Ich kann keine finden, Danke nochmal. – MrROY

    +0

    @MrROY: Dieser Abschnitt von Effective Go behandelt den Unterschied: http://golang.org/doc/effective_go.html#pointers_vs_values. Im Wesentlichen, wenn der Empfänger (T) ist, erhält Ihre Methode eine * Kopie * des Objekts, von dem sie aufgerufen wird, und kann sie daher nicht ändern. Wenn der Empfänger (* T) ist, erhält er einen Zeiger darauf. –

    +0

    In Fällen, in denen die Anzahl der Go-Routinen zur Kompilierzeit nicht genau bekannt ist, gibt es einen Grund, in jeder erstellten Goroutine keinen 'wg.Add (1)' 'defer.wg.Done()' aufzurufen? – matthias

    -4

    Antwort auf Frage 1: Wie jimt vorgeschlagen, sync/Atom hat Funktionen für atomar einen Zähler zu aktualisieren, die Ihnen nützlich sein können. Reduzieren Sie den Wert von DATA_SIZE_PER_THREAD, oder besser noch, verwenden Sie das Programm

    package main 
    func main() {} 
    

    , die in einer Art und Weise efficent die gleiche Ausgabe wie Ihr Programm erzeugt:

    Antwort auf Frage 2.

    Im Ernst, ich verstehe, dass Sie ein kleines Programm geschrieben haben, um einige Konzepte zu erkunden. Ihr Programm enthält jedoch eine Reihe von Problemen. Dies ist nicht die Zeit, um sich um Geschwindigkeit zu sorgen, es ist die Zeit, einige grundlegende Konzepte zu lernen.

    +0

    Keine hilfreiche Antwort. – OneOfOne

    Verwandte Themen