I'd like to make sure that we're starting a goroutine by calling a function with the right arguments.
For example:
func MyTest(t *testing.T) {
service.EXPECT().MyMockFunc(1)
service.MyFunc()
}
func MyFunc() {
go MyMockFunc(1)
}
When I run this test, it fails because (I believe) MyMockFunc only gets called after the test has already finished running.
Is there a way to test that I started a goroutine by calling a function with the right arguments?
Note: Ideally, I'd like to keep the arguments I pass to MyMockFunc as is (not add a channel arg for instance).
Using a channel and assuming you can fire the goroutine from the test:
package main
import (
"fmt"
"testing"
"time"
)
func MyMockFunc(n int) int {
fmt.Println("MyMockFunc is called")
time.Sleep(5 * time.Second)
return n + 1
}
func TestMyMockFunc(t *testing.T) {
result := make(chan int)
go func() {
result <- MyMockFunc(1)
}()
if <-result != 2 {
t.Fatalf("Expecting 2")
}
}
Related
There's built-in init() function for package initialization. Why not fini for destruction ? For example, I initial a goroutine pool inside my package and I want to all goroutines in the pool to finish their task before exiting instead of being forced to exit when the whole program exit.
If there's fini function. I can use sync.Wait there to fulfill my goal.
Another merit for built-in init is that it can and only can be called once,which will be my own concern if I use user-define functions as alternatives for them.
Try the following code:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
defer fini()
wg.Add(1)
go routine()
fmt.Println("... in progress ... ")
}
func fini() {
wg.Wait()
fmt.Println("Done")
}
func init() {
fmt.Println("Hi")
}
func routine() {
fmt.Println("Doing somthing ...")
time.Sleep(1000 * time.Millisecond)
wg.Done()
}
var wg sync.WaitGroup
Run:
$ go run .
Hi
... in progress ...
Doing somthing ...
Done
Doing fini in function main can solves my problem
I tried to implement a locking version of reading/writing from a map in golang, but it doesn't return the desired result.
package main
import (
"sync"
"fmt"
)
var m = map[int]string{}
var lock = sync.RWMutex{}
func StoreUrl(id int, url string) {
for {
lock.Lock()
defer lock.Unlock()
m[id] = url
}
}
func LoadUrl(id int, ch chan string) {
for {
lock.RLock()
defer lock.RUnlock()
r := m[id]
ch <- r
}
}
func main() {
go StoreUrl(125, "www.google.com")
chb := make(chan string)
go LoadUrl(125, chb);
C := <-chb
fmt.Println("Result:", C)
}
The output is:
Result:
Meaning the value is not returned via the channel, which I don't get. Without the locking/goroutines it seems to work fine. What did I do wrong?
The code can also be found here:
https://play.golang.org/p/-WmRcMty5B
Infinite loops without sleep or some kind of IO are always bad idea.
In your code if you put a print statement at the start of StoreUrl, you will find that it never gets printed i.e the go routine was never started, the go call is setting putting the info about this new go routine in some run queue of the go scheduler but the scheduler hasn't ran yet to schedule that task. How do you run the scheduler? Do sleep/IO/channel reading/writing.
Another problem is that your infinite loop is taking lock and trying to take the lock again, which will cause it to deadlock. Defer only run after function exit and that function will never exit because of infinite loop.
Below is modified code that uses sleep to make sure every execution thread gets time to do its job.
package main
import (
"sync"
"fmt"
"time"
)
var m = map[int]string{}
var lock = sync.RWMutex{}
func StoreUrl(id int, url string) {
for {
lock.Lock()
m[id] = url
lock.Unlock()
time.Sleep(1)
}
}
func LoadUrl(id int, ch chan string) {
for {
lock.RLock()
r := m[id]
lock.RUnlock()
ch <- r
}
}
func main() {
go StoreUrl(125, "www.google.com")
time.Sleep(1)
chb := make(chan string)
go LoadUrl(125, chb);
C := <-chb
fmt.Println("Result:", C)
}
Edit: As #Jaun mentioned in the comment, you can also use runtime.Gosched() instead of sleep.
Usage of defer incorrect, defer execute at end of function, not for statement.
func StoreUrl(id int, url string) {
for {
func() {
lock.Lock()
defer lock.Unlock()
m[id] = url
}()
}
}
or
func StoreUrl(id int, url string) {
for {
lock.Lock()
m[id] = url
lock.Unlock()
}
}
We can't control the order of go routine, so add time.Sleep() to control the order.
code here:
https://play.golang.org/p/Bu8Lo46SA2
I just want to do repetitive background tasks in Go, using time.AfterFunc,But seems something wrong with the logic.
The out put just:
interval call
interval call
But at least 5 times to call the function if all things went normal.
package main
import (
"fmt"
"time"
"os"
"os/signal"
)
type Timer struct {
Queue chan *TimeCall
}
func NewTimer(l int) *Timer {
timer := new(Timer)
timer.Queue = make(chan *TimeCall,l)
return timer
}
type TimeCall struct {
timer *time.Timer
callback func()
}
func (this *TimeCall) CallBack() {
defer func() { recover() }()
if this.callback != nil {
this.callback()
}
}
func (this *Timer) AfterFunc(d time.Duration, callback func()) *TimeCall {
call := new(TimeCall)
call.callback = callback
call.timer = time.AfterFunc(d, func() {
this.Queue <- call
})
return call
}
type PipeService struct {
TimeCall *Timer
}
func (this *PipeService) AfterFunc(delay time.Duration, callback func()) *TimeCall {
return this.TimeCall.AfterFunc(delay, callback)
}
func (this *PipeService) IntervalCall(interval time.Duration, callback func()) {
this.TimeCall.AfterFunc(interval,func(){
if callback != nil {
callback()
}
this.AfterFunc(interval,callback)
})
}
func (this *PipeService) Run(closeSig chan bool) {
for {
select {
case <-closeSig:
return
case call := <-this.TimeCall.Queue:
call.CallBack()
}
}
}
func main() {
var closeChan chan bool
InsPipeService := &PipeService{TimeCall: NewTimer(10)}
InsPipeService.IntervalCall(2*time.Second,func(){
fmt.Println("interval call")
})
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
go func(){
InsPipeService.Run(closeChan)
}()
time.Sleep(10*time.Second)
}
Run Code
time.AfterFunc() returns a *time.Timer, quoting form its doc:
The Timer type represents a single event. When the Timer expires, the current time will be sent on C, unless the Timer was created by AfterFunc.
The time.Timer returned by time.AfterFunc() does not repeat, so what you see is perfectly normal: in your PipeService.IntervalCall() you execute the callback immediately, and it gets executed after the timeout.
Also note that you pass 2 as interval for the PipeService.IntervalCall() method. This interval parameter is of type time.Duraion. So when you pass 2, that won't be 2 seconds (but actually 2 nanoseconds). You should pass a value constructed from constants from the time package like:
InsPipeService.IntervalCall(2 * time.Second, func(){
fmt.Println("interval call")
})
If you want repetition, use time.Ticker. For example the following code prints a message in every 2 seconds:
t := time.NewTicker(2 * time.Second)
for now := range t.C {
fmt.Println("tick", now)
}
Or simply if you don't need the Ticker and you don't want to shut it down:
c := time.Tick(2 * time.Second)
for now := range c {
fmt.Println("tick", now)
}
set time interval then call Start it will run user Job on each time intervals. set Enabled to false to stop it.
My Sample:
package main
import (
"fmt"
"sync"
"time"
)
type IntervalTimer struct {
Interval time.Duration
Enabled bool
Job func()
Wg sync.WaitGroup
}
func (it *IntervalTimer) isr() {
if it.Enabled {
it.Job()
time.AfterFunc(it.Interval, it.isr)
} else {
it.Wg.Done()
}
}
//trigger
func (it *IntervalTimer) Start() {
if it.Enabled {
it.Wg.Add(1)
time.AfterFunc(it.Interval, it.isr)
}
}
// user code:
var n int = 5
var it *IntervalTimer
func uerTask() {
fmt.Println(n, time.Now()) // do user job ...
n--
if n == 0 {
it.Enabled = false
}
}
func main() {
it = &IntervalTimer{Interval: 500 * time.Millisecond, Enabled: true, Job: uerTask}
it.Start()
//do some job ...
it.Wg.Wait()
fmt.Println("Bye")
}
I AM USING IN CRON PKG https://github.com/jasonlvhit/gocron/blob/master/gocron.go
import (
"fmt"
"time"
"github.com/claudiu/gocron"
)
func task() {
fmt.Println("I am runnning task.", time.Now())
}
func vijay() {
fmt.Println("I am runnning vijay.", time.Now())
}
func main() {
go test()
gocron.Start()
s := gocron.NewScheduler()
gocron.Every(5).Seconds().Do(task)
gocron.Every(10).Seconds().Do(vijay)
<-s.Start()
}
func test() {
time.Sleep(20 * time.Second)
gocron.Clear()
fmt.Println("All task removed")
}
My problem is after removing all job, my program is still executing
i want to break the exection after removing all jobs
please help me out ,i am not able to find out how to do it ,
i tried to change the PKG source code also but not able to find out the way to do it
thank you all
First, you're creating a new scheduler, and waiting on it, but using the default scheduler to run your jobs.
Next, you're blocking on the channel returned by the Start() method. Close that channel to unblock the receive operation. This will also exit the main loop in the cron program if you aren't immediately exiting from main.
func main() {
ch := gocron.Start()
go test(ch)
gocron.Every(5).Seconds().Do(task)
gocron.Every(10).Seconds().Do(vijay)
<-ch
}
func test(stop chan bool) {
time.Sleep(20 * time.Second)
gocron.Clear()
fmt.Println("All task removed")
close(stop)
}
which effectively is the same as
func main() {
gocron.Start()
gocron.Every(5).Seconds().Do(task)
gocron.Every(10).Seconds().Do(vijay)
time.Sleep(20 * time.Second)
gocron.Clear()
fmt.Println("All task removed")
}
If you're exiting immediately, it doesn't really matter if you call Clear() first and then stop the scheduler, you can simply exit the program.
JimB rights. But I don't know why do you use gocron methods and the s methods. This example works fine:
package main
import (
"fmt"
"time"
"github.com/claudiu/gocron"
)
func task() {
fmt.Println("I am runnning task.", time.Now())
}
func vijay() {
fmt.Println("I am runnning vijay.", time.Now())
}
func main() {
s := gocron.NewScheduler()
s.Every(2).Seconds().Do(task)
s.Every(4).Seconds().Do(vijay)
sc := s.Start() // keep the channel
go test(s, sc) // wait
<-sc // it will happens if the channel is closed
}
func test(s *gocron.Scheduler, sc chan bool) {
time.Sleep(8 * time.Second)
s.Clear()
fmt.Println("All task removed")
close(sc) // close the channel
}
I wrote a little worker queue using buffered channels.
I want to have the ability to "restart" this worker.
But when I do so I get a panic saying "panic: close of closed channel".
Actually I don't understand why its a closed channel because it shouldn't be closed any more after the make.
Here is the example code (http://play.golang.org/p/nLvNiMaOoA):
package main
import (
"fmt"
"time"
)
type T struct {
ch chan int
}
func (s T) reset() {
close(s.ch)
s.ch = make(chan int, 2)
}
func (s T) wrk() {
for i := range s.ch {
fmt.Println(i)
}
fmt.Println("close")
}
func main() {
t := T{make(chan int, 2)}
for {
go t.wrk()
time.Sleep(time.Second)
t.reset()
}
}
Can you tell me what I'm doing wrong there?
The problem is that you have a value receiver in your reset function which means that s will be copied and you don't see the effects on your t variable in the loop.
To fix that, make it a pointer receiver:
func (s *T) reset() {
close(s.ch)
s.ch = make(chan int, 2)
}
For more info on this topic see Effective Go.