p_mcp
p_mcp

Reputation: 2801

Run threads for a certain amount of time - before being killed

How do I run 10 threads, for 30 seconds each, and then return to the program execution? For example, I want

  1. 10 threads to be spawned and ran for 30 seconds.
  2. Then all threads killed
  3. Then second() to run (i.e. after all threads have finished executing)

So far I have the following however, when I do this, the threads (obviously) keep executing and CPU usage remains at 100% after 30s:

func main(){
    for i := 0; i < 10; i++{
        go thread()
    }
    time.Sleep(30 * time.Second)
    second()
}

func thread() {
    for {
        // Do stuff
    }
}

Upvotes: 1

Views: 2024

Answers (2)

Nathan Hyland
Nathan Hyland

Reputation: 860

You could use Golang context. Here's some of my code when I was learning it.

package main

import (
    "fmt"
    "log"
    "time"

    "golang.org/x/net/context"
)

func main() {
    someHandler()
}

func someHandler() {
    //Create a new context with a timeout duration of six seconds. You also get a cancel function you can call early. 
    //You can also have context end at a certain time, instead of a timeout
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(6))

    for i := 0; i < 5; i++ {
        go doStuff(ctx, i)
    }

    select {
    case <- ctx.Done():
        log.Println("Got a cancel request")
        return
    case <-time.After(time.Second * time.Duration(5)):
        //Here I'm actually ending it earlier than the timeout with cancel().
        log.Println("Timed out")
        cancel()
    }

}

func doStuff(ctx context.Context, num int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Done working")
            return
        default:
            fmt.Println("go", num, "working")
        }
        time.Sleep(time.Second)
    }
}

Outputs:

$ go run app.go 
go 0 working
go 4 working
go 2 working
go 3 working
go 1 working
go 1 working
go 0 working
go 4 working
go 3 working
go 2 working
go 0 working
go 4 working
go 2 working
go 1 working
go 3 working
go 4 working
go 3 working
go 0 working
go 2 working
go 1 working
go 3 working
go 4 working
go 1 working
go 0 working
go 2 working
2016/10/01 23:25:23 Timed out

Upvotes: 2

syllabix
syllabix

Reputation: 2295

Use a wait group and a "done" channel to cancel running go routines and and synchronize their exits before calling second. Some really cool stuff on the topic can be read here https://blog.golang.org/pipelines.

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    //create a "done" channel that will be used to signal to all go routines running thread to quit
    done := make(chan interface{})

    //create a wait group to sync all go routines before continuing program execution
    wg := new(sync.WaitGroup)

    for i := 0; i < 10; i++ {
        //increment waitgroup
        wg.Add(1)
        go thread(done, wg)
    }
    //zzzz...
    time.Sleep(30 * time.Second)
    //close the done channel
    close(done)
    //wait for all go routines to quit
    wg.Wait()
    //move on
    second()
}

func thread(done chan interface{}, wg *sync.WaitGroup) {
    defer wg.Done()
    working := true
    for working {
        //use a select statement to do work until done channel is closed
        select {
        case <-done:
            //end the loop
            working = false
        default:
            fmt.Println("doing work...")
        }
    }
}

func second() {
    fmt.Println("program complete")
}

Upvotes: 1

Related Questions