Wie kann man prüfen, ob ein Kanal geschlossen ist oder nicht, ohne ihn zu lesen?

Dies ist ein gutes Beispiel der Arbeitnehmer & controller-Modus in Go geschrieben von @Jimt, in Antwort auf
"Gibt es eine elegante Möglichkeit, um zu pausieren & resume andere goroutine in golang?"

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

//Possible worker states.
const (
    Stopped = 0
    Paused  = 1
    Running = 2
)

//Maximum number of workers.
const WorkerCount = 1000

func main() {
    //Launch workers.
    var wg sync.WaitGroup
    wg.Add(WorkerCount + 1)

    workers := make([]chan int, WorkerCount)
    for i := range workers {
        workers[i] = make(chan int)

        go func(i int) {
            worker(i, workers[i])
            wg.Done()
        }(i)
    }

    //Launch controller routine.
    go func() {
        controller(workers)
        wg.Done()
    }()

    //Wait for all goroutines to finish.
    wg.Wait()
}

func worker(id int, ws <-chan int) {
    state := Paused //Begin in the paused state.

    for {
        select {
        case state = <-ws:
            switch state {
            case Stopped:
                fmt.Printf("Worker %d: Stopped\n", id)
                return
            case Running:
                fmt.Printf("Worker %d: Running\n", id)
            case Paused:
                fmt.Printf("Worker %d: Paused\n", id)
            }

        default:
            //We use runtime.Gosched() to prevent a deadlock in this case.
            //It will not be needed of work is performed here which yields
            //to the scheduler.
            runtime.Gosched()

            if state == Paused {
                break
            }

            //Do actual work here.
        }
    }
}

//controller handles the current state of all workers. They can be
//instructed to be either running, paused or stopped entirely.
func controller(workers []chan int) {
    //Start workers
    for i := range workers {
        workers[i] <- Running
    }

    //Pause workers.
    <-time.After(1e9)
    for i := range workers {
        workers[i] <- Paused
    }

    //Unpause workers.
    <-time.After(1e9)
    for i := range workers {
        workers[i] <- Running
    }

    //Shutdown workers.
    <-time.After(1e9)
    for i := range workers {
        close(workers[i])
    }
}

Aber dieser code hat auch ein Problem: Wenn Sie entfernen möchten, ein Arbeiter-Kanal in workers wenn worker() beendet, dead lock passiert.

Wenn Sie close(workers[i]) nächsten mal-controller schreibt in es wird eine Panik verursachen, da gehen Sie nicht schreiben können in einem geschlossenen Kanal. Wenn Sie eine mutex zu schützen, dann wird es fest auf workers[i] <- Running da die worker ist lese nicht alles von den Kanal und schreiben werden gesperrt, und mutex dazu, dass ein dead lock. Sie können auch geben Sie einen größeren Puffer ein Kanal als work-around, aber es ist nicht gut genug.

So, ich denke, der beste Weg, dies zu lösen, ist worker() engen Kanal, wenn beendet, wenn der controller einen Kanal gefunden hat, geschlossen ist, überspringen Sie es und tun nichts. Aber ich kann nicht herausfinden, wie um zu überprüfen, ein Kanal ist bereits geschlossen, oder nicht in dieser situation. Wenn ich versuche zu Lesen, den Kanal-controller, der controller blockiert werden. Also ich bin sehr verwirrt jetzt.

PS: die Wiederherstellung der erhabene Panik ist, was ich versucht habe, aber es wird in der Nähe goroutine die Panik ausgelöst. In diesem Fall wird es controller, so dass es keine Verwendung.

Immer noch, ich denke, es ist nützlich für Go-team zu implementieren Sie diese Funktion in der nächsten version Gehen.

Kommentar zu dem Problem - Öffnen
Griff Zustand Ihrer Arbeiter! Wenn Sie in der Nähe Kanal, keine Notwendigkeit zu schreiben, um es wieder. Kommentarautor: jurka
Hier habe ich dieses: github.com/atedja/go-tunnel. Kommentarautor: atedja

InformationsquelleAutor der Frage Reck Hou | 2013-04-19

Schreibe einen Kommentar