Let's talk about gorutines
I have to say that my relationship with the goruntins not is the bests, However, I am deeply attracted to gorutins.
Ok, let’s skip the romantic part and get down to the nitty-gritty.
I have a dream
I have a dream? Yes, this question should be “I have a problem”, but it’s not enought funny to admit that I do.
In an ideal world I would have a lot of heavy tasks, running concurrently that I could cancel at any time, without worrying about memory leaks, race conditions or synchronization scheduling. Anyway, I have to think about it.
Dilema
Suppose I need to find a word (password), and I know, that it can be found in many password dictionaries, but I don’t have time to go through a dictionary one by one.
I will explain it in more detail in the code. Take it easy!
Go to code
We need to make these dictionaries accessible by name.
fs, err := os.ReadDir("./")
if err != nil {
panic(err)
}
dictionaries := make([]string, 0, len(fs)-1)
for _, f := range fs {
if f.Name() != "main.go" {
dictionaries = append(dictionaries, f.Name())
}
}
Are you ready?
At this point things start to get complicated. Although there are many ways to do it, this way is quite illustrative.
We need a for and a select to capture when something significant to the purpose of our program has occurred. Either the context has closed or a goruntine has already found the word we were looking for.
// we have to wait for at least one goruntine to find the word.
wg := sync.WaitGroup{}
wg.Add(1)
// channel to receive the dictionary
out := make(chan string)
// create context to cancel gorutins
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context, wg *sync.WaitGroup) {
for {
select {
case <-ctx.Done():
log.Println("Close another goruntines")
return
case dictionary := <-out:
wg.Done()
log.Println("WINNER DICTIONARY: ", dictionary)
cancel()
}
}
}(ctx, &wg)
for _, p := range dictionaries {
go Search(ctx, "andrew", p, out) // Set path
}
wg.Wait()
log.Println("Program finished")
You can read it carefully, it doesn’t look like it, but there is a lot here.
Finally, the function does all the work.
func Search(ctx context.Context, target, path string, out chan string) {
readFile, err := os.Open(path)
if err != nil {
fmt.Printf("cannot open file on path %s - error %v", path, err)
return
}
fileScanner := bufio.NewScanner(readFile)
fileScanner.Split(bufio.ScanLines)
for fileScanner.Scan() {
// time.Sleep(time.Second * 1) // You can uncomment this linea to see this process in more details
// add sleep to simulate a to too large file
log.Println("[+] - ", path, " Palabra: ", fileScanner.Text())
if fileScanner.Text() == target {
out <- path
break
}
}
}
Run and conclusion
If you have fun programming this will be a piece of cake, goruntines require a lot of practice and dedication. I have actually practiced with this article. You can see the repo github.com/juanmachuca95/search_go
This would be the final result
go run main.go
I am very new to writing articles, send me your feedback to keep improving.
Thank you so much, for visiting me.
Happy code!!