只允许一次接收值的通道 – golang

huangapple go评论91阅读模式
英文:

Channel accepting value only once - golang

问题

我刚刚开始学习Go语言。以下是我从下面的代码中尝试实现的目标。

主函数创建了5个线程用于执行**searchAndLog()**函数。该函数接收一个通道,通过该通道接收一个目录路径,然后在该目录中搜索名为".DS_Store"的文件,并对其进行某些操作。

主函数还创建了另一个线程用于执行**"filepath.Walk()"**函数。该线程将遍历传递给它的目录,并对遇到的每个目录(在walkFunc()中)执行select语句,将目录路径传递给5个线程中的一个。

但是当我运行这个程序时,**"filepath.Walk()"遇到的前五个目录会被传递给五个线程,但是一旦它将一个目录传递给每个线程一次后,它就停止了。select中的通道dir[0]dir[5]**不会再次接收值。

我做错了什么?

package main

// 一个简单的多线程程序,用于计算所有".DS_Store"文件占用的数量和总磁盘空间。
// 它还记录每个文件的位置和大小

import (
  "fmt"
  "io/ioutil"
  "os"
  "flag"
  "path/filepath"
)

// 如果路径是目录,则返回true,否则返回false
func isDirectory(path string) bool {
  file, err := os.Open(path)
  if err != nil {
    fmt.Println(err)
    return false
  }
  defer file.Close()
  fi, err := file.Stat()
  if err != nil {
    fmt.Println(err)
    return false
  }
  return (fi.Mode()).IsDir()
}

func main() {

  // 用于传递要生成的线程数的命令行标志
  var numThreads int
  flag.IntVar(&numThreads, "t", 5, "线程数")
  flag.Parse()
  fmt.Println("numThreads: ", numThreads)

  // 5个用于5个线程的通道
  var dir [5]chan string
  for i, _ := range dir {
    dir[i] = make(chan string)
  }

  // 这是将传递给filepath.Walk()的函数
  // 只有当路径指向目录时,才会执行"select"
  walkFunc := func(path string, info os.FileInfo, err error) error {
    fmt.Println("Visited: ", path)
    if isDirectory(path) {
      select {
        case dir[0] <- path:
          fmt.Println("Thread: 1")
        case dir[1] <- path:
          fmt.Println("Thread: 2")
        case dir[2] <- path:
          fmt.Println("Thread: 3")
        case dir[3] <- path:
          fmt.Println("Thread: 4")
        case dir[4] <- path:
          fmt.Println("Thread: 5")
        }
    }
    return nil
  }

  // 创建5个searchAndLog()线程
  for i := 0; i < numThreads; i++ {
    go searchAndLog(dir[i], i)
  }

  go filepath.Walk("/Users/nikhil/Workspace", walkFunc)

  var input string
  fmt.Scanln(&input)
}

// 通过id来标识线程的println语句
func searchAndLog(dirpath chan string, id int) {
  directory := <- dirpath
  fmt.Println("Thread # ", id + 1, directory)
  files, _ := ioutil.ReadDir(directory)
  for _, f := range files {
    if f.Name() == ".DS_Store" {
      fmt.Println("Thread # ", id + 1, f.Size())
    }
  }
}

编辑正如Damsham指出的线程在接收到值后退出使用他建议的代码替换线程创建循环以修复代码
英文:

I am just starting out with go. Here is what I am trying to achieve from the following code.

Main function creates 5 threads for searchAndLog(). This function accepts a channel through which it will receive a path to a directory and it will search for a file called ".DS_Store" and then do something with it.

main function then creates another thread for "file path.Walk()". This thread will walk directory passed to it and for each directory which it comes across (in walkFunc()) it will execute select statement passing directory path to one of the 5 threads.

But when I run this program, first five directories which "filepath.Walk()" encounters it passes it to five threads, but once it has passed a directory to each of the threads once, it stops. Channels dir[0] to dir[5] in select won't accept value second time around.

What am I doing wrong?

package main
// A simple multithreaded program to calculate how many
// and total disk space occupyed by all &quot;.DS_Store&quot; files.
// It also logs the location of each file along with its
// size
import (
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;os&quot;
&quot;flag&quot;
&quot;path/filepath&quot;
)
// Returns true if path is a directory otherwise false
func isDirectory(path string) bool {
file, err := os.Open(path)
if err != nil {
fmt.Println(err)
return false
}
defer file.Close()
fi, err := file.Stat()
if err != nil {
fmt.Println(err)
return false
}
return (fi.Mode()).IsDir()
}
func main() {
// Command line flag to pass in how many threads to swapn
var numThreads int
flag.IntVar(&amp;numThreads, &quot;t&quot;, 5, &quot;number of threads&quot;)
flag.Parse()
fmt.Println(&quot;numThreads: &quot;, numThreads)
// 5 Channels for 5 threads
var dir [5]chan string
for i, _ := range dir {
dir[i] = make(chan string)
}
// This is the function that will be passed to filepath.Walk()
// &quot;select&quot; will be executed only if path points to directory
walkFunc := func(path string, info os.FileInfo, err error) error {
fmt.Println(&quot;Visited: &quot;, path)
if isDirectory(path) {
select {
case dir[0] &lt;- path:
fmt.Println(&quot;Thread: 1&quot;)
case dir[1] &lt;- path:
fmt.Println(&quot;Thread: 2&quot;)
case dir[2] &lt;- path:
fmt.Println(&quot;Thread: 3&quot;)
case dir[3] &lt;- path:
fmt.Println(&quot;Thread: 4&quot;)
case dir[4] &lt;- path:
fmt.Println(&quot;Thread: 5&quot;)
}
}
return nil
}
// Create 5 threads of searchAndLog()
for i := 0; i &lt; numThreads; i++ {
go searchAndLog(dir[i], i)
}
go filepath.Walk(&quot;/Users/nikhil/Workspace&quot;, walkFunc)
var input string
fmt.Scanln(&amp;input)
}
// id is passed to identify the thread in the println statements
func searchAndLog(dirpath chan string, id int) {
directory := &lt;- dirpath
fmt.Println(&quot;Thread # &quot;, id + 1, directory)
files, _ := ioutil.ReadDir(directory)
for _, f := range files {
if f.Name() == &quot;.DS_Store&quot; {
fmt.Println(&quot;Thread # &quot;, id + 1, f.Size())
}
}
}

EDIT: As Damsham pointed out as threads exits after accepting value once. Replace thread creation loop with code that he suggested to fix the code.

答案1

得分: 3

因为一旦searchAndLog在通道上接收到内容,它将执行相应的操作并退出。因此,在处理完5个目录后,所有正在运行searchAndLog的goroutine都已经结束。可以尝试以下代码:

// 创建5个searchAndLog()线程
for i := 0; i < numThreads; i++ {
  go func(i int) {
     for {
        searchAndLog(dir[i], i)
     }
  }(i)
}
英文:

Because once searchAndLog receives something on the channel, it will do its stuff and exit. Therefore after 5 directories, all of your goroutines that were running searchAndLog are now gone. Try this:

// Create 5 threads of searchAndLog()
for i := 0; i &lt; numThreads; i++ {
go func(i int) {
for {
searchAndLog(dir[i], i)
}
}(i)
}

huangapple
  • 本文由 发表于 2014年7月26日 09:37:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/24966675.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定