質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

2470閲覧

go言語でタイマーAPIの作成について

退会済みユーザー

退会済みユーザー

総合スコア0

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

1クリップ

投稿2017/06/08 09:49

Go言語と、Framework [echo]を利用してAPIを作成しております。

特定のデータごとのタイマーを、Web APIを利用して実装したいと考えております。

処理の流れとしましては下記のような実装を考えております。

1.タイマー開始用のAPIにパラメーター付きでアクセスし、goroutineで、別プロセスを起動
2.タイマー停止用のAPIにパラメーター付きでアクセスし、既に動いているgoroutineを検知し、プロセスを停止

実際に実装を行い別プロセス毎に、動かせているような実装はできました?…。

しかし、2の処理を行うと、プログラムが停止しません。

そもそも、実現しようとしていることに無理があるのかもしれませんが、
アドバイスを頂けると幸いです。

大変、お手数をおかけしますが、宜しくお願い致します。

▼main.go

go

1 2 3package main 4 5import ( 6 "sample/handler" 7 8 _ "github.com/go-sql-driver/mysql" 9 "github.com/labstack/echo" 10 "sample/data" 11) 12 13func main() { 14 15 // トップディレクトリ 16 e := echo.New() 17 18 ch := make(chan bool) 19 20 // 製品を監視するタイマー 21 var ids = make([]*data.Ids, 0) 22 23 e.GET("start", handler.Start(ch, ids)) 24 e.GET("stop", handler.Stop(ch, ids)) 25 26 e.Logger.Fatal(e.Start(":1323")) 27} 28 29

▼handler/start.go

Go

1 2package handler 3 4import ( 5 "github.com/labstack/echo" 6 "net/http" 7 "fmt" 8 "time" 9 "strconv" 10 "sample/data" 11) 12 13func Start(ch chan bool, ids []*data.Ids) echo.HandlerFunc { 14 15 return func(c echo.Context) error { 16 17 id := c.QueryParam("id") 18 limit, _ := strconv.Atoi(c.QueryParam("limit")) 19 20 go func() { 21 22 //result = append(result, &(data.Ids{Id: v.Id})) 23 ids = append(ids, &(data.Ids{Id:id})) 24 25 t := time.NewTicker(3 * time.Second) // 1秒おきにチェック 26 n := 0 27 for { 28 select { 29 case <-t.C: 30 if ( limit < n) { 31 fmt.Print("over") 32 } 33 fmt.Print("id =" + id) 34 fmt.Println(n) 35 n++ 36 case <-ch: 37 fmt.Println("stop") 38 39 t.Stop() 40 } 41 } 42 43 }() 44 45 return c.JSON(http.StatusCreated, "ok") 46 } 47 48} 49

▼handler/stop.go

Go

1package handler 2 3import ( 4 "github.com/labstack/echo" 5 "net/http" 6 "sample/data" 7 "fmt" 8) 9 10func Stop(ch chan bool, ids []*data.Ids) echo.HandlerFunc { 11 12 return func(c echo.Context) error { 13 id := c.QueryParam("id") 14 arrayRemove(ids, id) 15 16 ch <- true 17 return c.JSON(http.StatusCreated, "ok") 18 } 19 20} 21 22func arrayRemove(arr []*data.Ids, str string) []*data.Ids { 23 24 fmt.Println("remove") 25 26 result := make([]*data.Ids, 0) 27 for _, v := range arr { 28 if v.Id != str { 29 result = append(result, &(data.Ids{Id: v.Id})) 30 fmt.Println(v.Id) 31 } 32 } 33 return result 34}

▼sample/data/ids.go

Go

1package data 2 3type Ids struct { 4 Id string 5}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

タイマーがID単位で発行されていますが、停止が1つのチャネルで行われているのでもし id=1 と id=2 で別のタイマーが作られたとしても ch を先にどちらが読むかは分かりません(タイミング次第です)。
また、id=1 に関するタイマーがリクエスト毎に作られてしまいますので、期待しない動作になります。
さらに goroutine のループが抜けていませんのでリクエストする度に goroutine が追加されます。

指摘ばかりだと分かりにくいかもしれませんので例を。

▼ids.go

go

1package main 2 3import "sync" 4import "time" 5 6type IdInfo struct { 7 Id string 8 t *time.Ticker 9} 10 11type IdMap map[string]*IdInfo 12 13var mu sync.Mutex 14 15func (idmap IdMap) StartTimer(id string) <-chan time.Time { 16 mu.Lock() 17 defer mu.Unlock() 18 19 info, ok := idmap[id] 20 if ok && info.t != nil { 21 // 既にあればどこかでループが回っているはず 22 return nil 23 } 24 idmap[id] = &IdInfo{Id: id, t: time.NewTicker(time.Second)} 25 return idmap[id].t.C 26} 27 28func (idmap IdMap) StopTimer(id string) { 29 mu.Lock() 30 defer mu.Unlock() 31 32 idinfo, ok := idmap[id] 33 if !ok { 34 return 35 } 36 idinfo.t.Stop() 37 idinfo.t = nil 38}

IdMap は全てのIDを管理するmapで、StartTimer/StopTimer に与えた引数に従って各々のタイマーを生成します。無ければ作ります。StopTimer で nil クリアしており完全にタイマーが停止するまで次のタイマーは作られません。

▼start.go

go

1package main 2 3import ( 4 "fmt" 5 "net/http" 6 "strconv" 7 8 "github.com/labstack/echo" 9) 10 11func Start(ids IdMap) echo.HandlerFunc { 12 13 return func(c echo.Context) error { 14 id := c.QueryParam("id") 15 if id == "" { 16 return c.JSON(http.StatusBadRequest, "ng") 17 } 18 limit, err := strconv.Atoi(c.QueryParam("limit")) 19 if err != nil { 20 limit = 3 21 } 22 t := ids.StartTimer(id) 23 if t == nil { 24 return c.JSON(http.StatusBadRequest, "ng") 25 } 26 27 go func() { 28 defer ids.StopTimer(id) 29 30 n := 0 31 for { 32 _, ok := <-t 33 if !ok { 34 break 35 } 36 if limit < n { 37 fmt.Printf("id = %v, over\n", id) 38 break 39 } 40 fmt.Printf("id = %v, count = %v\n", id, n) 41 n++ 42 } 43 }() 44 45 return c.JSON(http.StatusCreated, "ok") 46 } 47 48}

既にタイマーが起動していれば ng を返す様にしています。

▼stop.go

go

1package main 2 3import ( 4 "net/http" 5 6 "github.com/labstack/echo" 7) 8 9func Stop(ids IdMap) echo.HandlerFunc { 10 11 return func(c echo.Context) error { 12 id := c.QueryParam("id") 13 14 ids.StopTimer(id) 15 16 return c.JSON(http.StatusCreated, "ok") 17 } 18 19}

▼main.go

go

1package main 2 3import "github.com/labstack/echo" 4 5func main() { 6 // トップディレクトリ 7 e := echo.New() 8 9 // 製品を監視するタイマー 10 var ids = make(IdMap) 11 12 e.GET("start", Start(ids)) 13 e.GET("stop", Stop(ids)) 14 15 e.Start(":1323") 16}

投稿2017/06/19 09:49

mattn

総合スコア5030

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問