协程与通道
协程(goroutine)是轻量级线程,可实现函数或方法与主程序流并行执行。使用go关键字:go func(){}。通道是协程直接的通讯管道,主要用于在协程间传输数据,即往通道写数据、从通道读数据。
通过chan关键字声明通道,可以使用var或:=两种方式声明,也可以声明待缓存的通道,语法如下:
channelname:= make(chan type, n)
举例:
datastream := make(chan string, 1)
往通道写数据:
datastream <- "data"
从通道读数据:
varname := <-datastream
关闭通道:
close(datastream)
go 超时机制
超时对于连接到外部资源或需要限制执行时间的场景来说非常重要。这是因为太长的服务器端处理将消耗太多的资源,导致并发性下降,甚至服务不可用。
利用select语句及并行协程实现超时,必须导入time包。然后使用time.after()参数创建通道,调用time.after(1 * time.second)将在1秒后填充通道。下面示例通过通道和select实现超时:
package main
import (
"fmt"
"time"
)
func main() {
datachannel:= make(chan string, 1)
go func() {
time.sleep(2 * time.second)
datachannel <- "result 1"
}()
select {
case results := <- datachannel:
fmt.println(results)
case <-time.after(1 * time.second):
fmt.println("timeout 1")
}
}
首先创建缓存通道datachannel,调用函数模拟复杂业务,2秒后从非阻塞通道返回结果。select语句实现超时。results := <- datachannel等待结果,time.after(1 * time.second)语句1秒后返回值,因此select首先等待1秒,超过1秒将超时。
下面利用该机制实现读取文件超时机制实现。
读取整个文件
go中读整个文件一般使用ioutil/os包中的read file()函数,读取整个文件值字节slice。ioutil包最好别用于读取大文件,对于小文件完全够用。
os包包含执行参数数组args,包括执行命令的所有参数,为字符串类型数组。
package main
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"time"
)
func main() {
filepath := os.args[1]
timeout, _ := strconv.parsefloat(os.args[2], 64)
// buffered channel of datastream
datastream := make(chan string, 1)
// run readfiletostring function in it's own goroutine and pass back it's
// response into datastream channel.
go func() {
data, _ := readfiletostring(filepath)
datastream <- data
close(datastream)
}()
// listen on datastream channel and a timeout channel - which ever happens first.
select {
case res := <-datastream:
fmt.println(res)
case <-time.after(time.duration(timeout) * time.second):
fmt.println("program execution out of time ")
}
}
func readfiletostring(file string) (string, error) {
content, err := ioutil.readfile(file)
// error encountered during reading the data
if err != nil {
return "", err
}
// convert bytes to string
return string(content), nil
}
我们可以使用不同的超时时间进行测试:
go run main.go text.txt 1.0 go run main.go text.txt 0.9
按行读文件
可以使用 bufio.scanner 按行读文件,使用bufio.newscanner(file)构造函数创建scanner,然后通过scan()和text()方法逐行读取内容。使用err()方法检查读取文件过程中的错误。
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"time"
)
func main() {
//get filepath and timeout on the terminal
filepath := os.args[1]
timeout, _ := strconv.parsefloat(os.args[2], 64)
//creating channels
datastream := make(chan string, 1)
readerr := make(chan error)
// run readfilelinebyline function in its own goroutine and pass back it's
// response into datastream channel.
go readfilelinebyline(filepath, datastream, readerr)
loop:
for {
// select statement will block this thread until one of the three conditions below is met
select {
case data := <-datastream:
// process each line
fmt.println(data)
case <-time.after(time.duration(timeout) * time.second):
fmt.println("program execution out of time ")
break loop
case err := <-readerr:
if err != nil {
log.fatal(err)
}
break loop
}
}
}
func readfilelinebyline(filepath string, data chan string, er chan error) {
// open file
file, err := os.open(filepath)
if err != nil {
fmt.println(err)
}
// close the file at the end of the program
defer file.close()
// read the file line by line using scanner
scanner := bufio.newscanner(file)
for scanner.scan() {
data <- scanner.text()
}
close(data) // close causes the range on the channel to break out of the loop
er <- scanner.err()
}
当然也可以使用不同超时时间进行测试,如超时场景:
go run main.go test.txt 0.1 # program execution out of time
按块方式读文件
对于非常大的文件使用块方式读取非常有用,无需把整个文件加载到内存中,每次读取固定块大小内容。下面readfilechunk函数中需要创建缓冲区,每次读取该缓冲区大小的内容,直到io.eof错误出现,表明已经到达文件结尾。
缓冲区大小、目标文件以及超时时间作为函数参数,其他逻辑与上面示例一致:
package main
import (
"fmt"
"io"
"log"
"os"
"strconv"
"time"
)
func main() {
datastream := make(chan string, 1)
filepath := os.args[1]
timeout, _ := strconv.parsefloat(os.args[2], 64)
chunksize, _ := strconv.atoi(os.args[3])
go readfilechunk (filepath, datastream, int64(chunksize))
select {
case resultdata := <- datastream:
fmt.println(resultdata)
case <-time.after(time.duration(timeout) * time.millisecond):
fmt.println("timeout")
}
}
func readfilechunk(filepath string, data chan string, chunksize int64) {
// open file
f, err := os.open(filepath)
if err != nil {
log.fatal(err)
}
// remember to close the file at the end of the program
defer f.close()
buf := make([]byte, chunksize)
for {
readtotal, err := f.read(buf)
if err != nil && err != io.eof {
log.fatal(err)
}
if err == io.eof {
break
}
data <- string(buf[:readtotal])
}
close(data)
}
总结
对于资源密集型程序正在执行时,golang超时机制是必要的,它有助于终止这种冗长的程序。本文通过示例展示了不同方式读取文件的超时机制实现。
到此这篇关于golang实现超时机制读取文件的方法示例的文章就介绍到这了,更多相关golang超时机制读取文件内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论