2017-10-14 23 views
0

Je dispose d'un fichier comme ceci:manière optimale à lire simultanément un fichier dans Go

NAME : a280 
COMMENT : drilling problem (Ludwig) 
TYPE : TSP 
DIMENSION: 280 
EDGE_WEIGHT_TYPE : EUC_2D 
NODE_COORD_SECTION 
    1 288 149 
    2 288 129 
    3 270 133 
    4 256 141 
    5 256 157 
    6 246 157 
    7 236 169 
    8 228 169 
    9 228 161 
10 220 169 
11 212 169 
12 204 169 
13 196 169 
14 188 169 
15 196 161 

et ainsi de suite ...

Les chiffres sont les cordons pour les villes à résoudre TSP. J'essaye d'écrire ceci à Golang. Maintenant, les instances peuvent être comme 200 villes, voire 40.000 villes. Je veux obtenir la meilleure solution possible, alors j'ai pensé que je devrais traiter ce fichier simultanément. J'ai le code suivant:

package main 

import (
    "bufio" 
    "fmt" 
    "os" 
    "regexp" 
    "strings" 
) 

func getCords(queue chan string) { 
    cords := regexp.MustCompile(`\s*\d+\s+\d+\s+\d+`) 
    for line := range queue { 
     if cords.MatchString(line) { 
      fmt.Println(line) 
     } 
    } 
} 

func readFile(fileName string) { 
    cords := make(chan string) 
    file := strings.NewReader(fileName) 
    go func() { 
     scanner := bufio.NewScanner(file) 
     for scanner.Scan() { 
      cords <- scanner.Text() 
     } 
     close(cords) 
    }() 
} 

// Menu - main program menu 
func Menu() { 
    reader := bufio.NewReader(os.Stdin) 
    fmt.Println("================== Projektowanie efektywnych algorytmów ==================") 
    fmt.Println("================== Zadanie nr 1 - Algorytm xyz    ==================") 
    // Wczytywanie pliku z danymi 
    // Format: Lp. X Y 
    fmt.Printf("\nPodaj nazwę pliku: ") 
    fileName, err := reader.ReadString('\n') 
    if err != nil { 
     fmt.Println(err) 
     return 
    } 
    readFile(fileName) 
} 

func main() { 
    Menu() 
} 

En fonction getCords je dois useregex, cuz les fichiers ont tendance à avoir une partie de l'information au début.

Le problème commence à readFile(). Je lance un goroutine, qui scanne le fichier ligne par ligne et récupère toutes les lignes sur le canal. Bien sûr, l'exécution le lance et va plus loin. Maintenant, le problème est, après l'appel go func(), je devrais essayer de lire à partir du canal. Les solutions que je trouve sur le SO, et sur Internet sont les suivantes:

func readFile(fileName string) { 
    cords := make(chan string) 
    file := strings.NewReader(fileName) 
    go func() { 
     scanner := bufio.NewScanner(file) 
     for scanner.Scan() { 
      cords <- scanner.Text() 
     } 
     close(cords) 
    }() 
    for i := 0; i < 100; i++ { 
     go getCords(cords) 
    } 
} 

donc te première exécution de getCords serait probablement faire encore rien, parce que le goroutine ne parviendrait pas à obtenir la ligne au canal rapide. Les prochaines itérations feraient probablement le travail, mais le problème est que je dois écrire un certain nombre, comme 100 dans cet exemple, et il peut être trop élevé, donc le canal se fermera comme 10 itérations et après c'est juste une perte de temps ou il peut être trop bas, et alors je ne recevrais tout simplement pas tous les résultats.

Comment résolvez-vous des problèmes comme celui-ci, les gars? Existe-t-il une manière optimale, ou dois-je coller avec certains waitGroups?

+0

commence par écrire une version séquentielle qui fonctionne, alors, trouver les plus lents parties du programme en utilisant le profilage, commence enfin à ajouter au programme concurrency, dans les endroits appropriés. En outre, https://github.com/AkshanshChahal/TSP-Golang –

+0

Vous n'ouvrez aucun fichier n'importe où dans votre code. 'file: = strings.NewReader (fileName)' ne s'ouvre pas et ne charge pas les données de 'fileName'. 'os.Open' fait cela: https://golang.org/pkg/os/#Open. – abhink

Répondre

1

Je pense que oui, il serait bon d'utiliser sync.WaitGroup pour s'assurer que tous les goroutines ont terminé leur travail. Une solution possible:

func getCords(queue Chas string, wg sync.WaitGroup) { 
    defer wg.Done() 
    // your code 
} 

func readFile(fileName string) { 
    cords := make(chan string) 
    file := strings.NewReader(fileName) 

    go func() { 
     scanner := bufio.NewScanner(file) 
     for scanner.Scan() { 
      cords <- scanner.Text() 
     } 
     close(cords) 
    }() 

    wg := sync.WaitGroup{} 
    for i := 0; i < 100; i++ { 
     wg.Add(1) 
     go getCords(cords, wg) 
    } 
    wg.Wait() 
} 
+0

Mais un goroutine fait toute la lecture à la chaîne. Donc, ce goroutine commence, et l'exécution va plus loin. Alors où dois-je appeler 'go getCords()' – Frynio

+0

J'ai mis à jour le code. Ce n'est pas un problème - parce que écrire à 'cordes <- scanner.Text()' sera bloqué. Parce qu'il n'y a personne à lire sur le canal tant que les goroutines 'getCoords' ne sont pas lancées. Vous pouvez également échanger ces deux blocs: démarrer les consommateurs 'getCoords' avant et plus tard démarrer le producteur - lecteur de fichiers. –

+0

À droite, j'ai oublié d'envoyer des blocs à la goroutine jusqu'à ce que quelqu'un lise la chaîne. Ne dois-je pas passer 'wg' par pointeur? Et pourquoi fais-tu la boucle for pour les itérations '100', quand on ne sait pas combien de lignes lira-t-il? – Frynio