SoFunction
Updated on 2025-03-04

Example of a method for Golang to implement timeout mechanism to read files

Coroutines and Channels

A coroutine is a lightweight thread that can implement functions or methods to execute in parallel with the main program stream. Use the go keyword: go func(){}. A channel is a direct communication pipeline of coroutines, which is mainly used to transmit data between coroutines, that is, to write data to the channel and read data from the channel.

Declare the channel through the chan keyword, you can declare it using var or :=, or you can declare the channel to be cached. The syntax is as follows:

channelName:= make(chan Type, n)

For example:

dataStream := make(chan string, 1)

Write data to the channel:

dataStream <- "data"

Read data from the channel:

varName := <-dataStream

Close the channel:

close(dataStream)

Go timeout mechanism

Timeout is very important for scenarios where connections to external resources or require limiting execution time. This is because server-side processing that is too long will consume too much resources, resulting in reduced concurrency and even unavailable services.

Use select statements and parallel coroutines to implement timeouts, and the time package must be imported. Then create the channel using the() parameter, and calling (1 * ) will fill the channel after 1 second. The following example implements timeout through channel and select:

package main
import (
    "fmt"
    "time"
)
func main() {
    dataChannel:= make(chan string, 1)

    go func() {
        (2 * )
        dataChannel <- "result 1"
    }()

    select {
        case results := <- dataChannel:
            (results)
        case <-(1 * ):
            ("timeout 1")
    }
}

First, create a cache channel dataChannel, call the function to simulate complex business, and return the result from the non-blocking channel after 2 seconds. The select statement implementation timeout. results := <- dataChannel waits for the result, the (1 * ) statement returns the value after 1 second, so select waits for 1 second first, and after 1 second, it will time out.

The following is to use this mechanism to realize the timeout mechanism for reading files.

Read the entire file

In Go, reading the entire file is generally done using the Read File() function in the ioutil/os package to read the entire file value byte slice. The ioutil package is best not to be used to read large files, as it is completely sufficient for small files.

The os package contains the execution parameter array Args, including all parameters of the execution command, and is an array of string types.

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"time"
)

func main() {

	filePath := [1]
	timeOut, _ := ([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:
		(res)
	case <-((timeOut) * ):
		("Program execution out of time ")
	}
}
func ReadFileToString(file string) (string, error) {
	content, err := (file)
	// error encountered during reading the data
	if err != nil {
		return "", err
	}
	// convert bytes to string
	return string(content), nil
}

We can use different timeouts for testing:

go run   1.0

go run   0.9

Read the file by line

You can use the file to read by line, create a Scanner using the (file) constructor, and then read the content line by line through the Scan() and Text() methods. Use the Err() method to check for errors in the process of reading the file.

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"
)

func main() {
	//get filepath and timeout on the terminal
	filePath := [1]
	timeOut, _ := ([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
			(data)
		case <-((timeOut) * ):
			("Program execution out of time ")
			break loop
		case err := <-readerr:
			if err != nil {
				(err)
			}
			break loop
		}
	}
}

func ReadFileLineByLine(filePath string, data chan string, er chan error) {
	// open file
	file, err := (filePath)
	if err != nil {
		(err)
	}
	// close the file at the end of the program
	defer ()

	// read the file line by line using scanner
	scanner := (file)

	for () {
		data <- ()
	}
	close(data) // close causes the range on the channel to break out of the loop
	er <- ()
}

Of course, you can also use different timeout times for testing, such as timeout scenarios:

go run   0.1
# Program execution out of time

Read files in block mode

It is very useful for very large files to read blocks, without loading the entire file into memory, and read fixed block size content every time. The following readFileChunk function needs to create a buffer, and read the content of the buffer size every time until an error occurs, indicating that the end of the file has been reached.

Buffer size, target file, and timeout as function parameters, other logic is consistent with the above example:

package main

import (
   "fmt"
   "io"
   "log"
   "os"
   "strconv"
   "time"
)

func main() {
   dataStream := make(chan string, 1)
   filePath := [1]
   timeOut, _ := ([2], 64)
   chunkSize, _ := ([3])
   go readFileChunk (filePath, dataStream, int64(chunkSize))
   select {
   case resultData := <- dataStream:
      (resultData)
   case <-((timeOut) * ):
      ("timeout")
   }

}

func readFileChunk(filePath string, data chan string, chunkSize int64) {
   // open file
   f, err := (filePath)
   if err != nil {
      (err)
   }
   // remember to close the file at the end of the program
   defer ()

   buf := make([]byte, chunkSize)
   for {
      readTotal, err := (buf)
      if err != nil && err !=  {
         (err)
      }

      if err ==  {
         break
      }

      data <- string(buf[:readTotal])
   }
   close(data)
}

Summarize

The Golang timeout mechanism is necessary when a resource-intensive program is being executed, which helps terminate such a lengthy program. This article shows the implementation of timeout mechanisms for reading files in different ways through examples.

This is the article about this example of how Golang implements the timeout mechanism to read files. For more related Golang timeout mechanism to read files, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!