douxitao8170 2017-07-04 18:08
浏览 173
已采纳

Golang非阻塞频道不起作用

I work the first time with goroutines and channels in go and do not come any further.

I have a websocket connection where every time a user connect a new goroutine is spawned. Now I want to stop this goroutine if the user disconnect from the websocket connection.

To manage the stop signale I have create a map of channels. Each entry can be identified by the users websocket connection. I pass the websocket connection, the map of channels for the stop signal and two other parameters to the goroutine. But the goroutine doesn't receive any values from the quit channel and I don't know why.

Here is the relevant code for the main package:

package main

import (
    "net/http"
    "time"

    "github.com/gorilla/websocket"
)

func wsHandler(w http.ResponseWriter, r *http.Request) {
    ...
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        return
    }
    defer ws.Close()

    data.Quit[ws] = make(chan bool)

    data.DB.ListenToTable(data.GetTableName(source), channel, data.Quit, ws)

    for {
        if _, _, err := ws.NextReader(); err != nil {
            data.Quit[ws] <- true

            ws.Close()
            break
        }
    }
}

And the code of the data package where the goroutine is created:

package data

var Quit = make(map[*websocket.Conn](chan bool))

func (db *rethinkDB) ListenToTable(name string, ch chan Data, quit map[*websocket.Conn](chan bool), ws *websocket.Conn) {
    go func(name string, ws *websocket.Conn) {
        for {
            select {
            case <-quit[ws]:
                fmt.Println("QUIT GOROUTINE")
                break
            default:
                res, err := r.Table(name).Changes().Run(db.session)
                if err != nil {
                    log.Fatalln(err)
                }

                var response DataFeed
                for res.Next(&response) {
                    response.NewVal.WS = ws
                    ch <- response.NewVal
                }

                if res.Err() != nil {
                    log.Println(res.Err())
                }
            }
        }
    }(name, ws)
}

I have also tried buffered channels or pass the channel instead of the map of channels to the goroutine but without success. The fmt.Println("QUIT GOROUTINE") command is never called and the goroutine isn't sopped.

I hope someone can help me and sorry if this question was already ask but I haven't found a solution that solves my problem.

  • 写回答

1条回答 默认 最新

  • dpnzf48660 2017-07-04 21:12
    关注

    First to make things easier:

    As far as I can see you do not need a global register for the quit channels. Just create a ch := make(chan bool) in main, pass it to ListenToTable (instead of the whole map of channels) and use it in the select. In main close(ch) it if you want to exit. But as you said, that doesn't solve your problem.

    Theoretically you are on the right track with closing the go routine. I took your sample code and made the following runnable code from it:

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
    
        chClose := make(chan bool)
        channel := make(chan string)
    
        ListenToTable("somestring", channel, chClose)
    
        time.Sleep(3 * time.Second)
        chClose <- true
        time.Sleep(1 * time.Second)
    }
    
    func ListenToTable(name string, ch chan string, chClose chan bool) {
        go func(name string) {
            for {
                select {
                case <-chClose:
                    fmt.Println("QUIT GOROUTINE")
                    return  // VERY IMPORTANT: not break!
                default:
                }
            }
        }(name)
    }
    

    The problem must be with something else in you code, probably blocked by something in the default section and not even executing the select. Try printing fmt.Println("something") before the select {. If that is not printed regularly then you have your answer.

    One more thing: As commented in the code above you cannot break out of a for { select { ... } } with a single break. You need to use a return (to exit the function) or another tactic (like a break with a label as Adrian suggested in the comments). The break will only exit the select, but not the for loop.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 stm32开发clion时遇到的编译问题
  • ¥15 lna设计 源简并电感型共源放大器
  • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)
  • ¥15 Vue3地图和异步函数使用
  • ¥15 C++ yoloV5改写遇到的问题
  • ¥20 win11修改中文用户名路径
  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入
  • ¥15 用土力学知识进行土坡稳定性分析与挡土墙设计
  • ¥70 PlayWright在Java上连接CDP关联本地Chrome启动失败,貌似是Windows端口转发问题
  • ¥15 帮我写一个c++工程