有老师能帮忙看看我的获取素数代码哪里有问题么,我只能取到一个素数3

reckless-huang · 2020-08-27 09:32:27 · 740 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2020-08-27 09:32:27 的主题,其中的信息可能已经有所发展或是发生改变。

package main

import (
    "fmt"
    _ "reflect"
    "sync"
    "time"
)

func writeData(intChan chan int) {
    for i := 1; i <= 8000; i++ {
        intChan <- i
        //fmt.Printf("writeData()写入数据%v", i)
    }
    close(intChan)
    //fmt.Println(intChan)
}

func judgData(intChan chan int, resultChan chan int, exitChan chan bool, done func()) {
    defer done()
    time.Sleep(time.Millisecond * 1000)
    flag := true
    //time.Sleep(time.Second *1)
    for {
        num, ok := <-intChan
        if !ok {
            fmt.Println("已从intChan读取所有数据")
            break
        }
        for i := 2; i < num; i++ {
            if num % i == 0 {
                flag = false
                break
            }
            if flag {
                resultChan <- num
                fmt.Printf("添加%v到resultChan\n", num)
            }
        }
    }
    fmt.Println("协程执行完毕")
    exitChan <- true
}
func main() {
    var wg sync.WaitGroup
    intChan := make(chan int, 10000)
    resultChan := make(chan int, 20000)
    exitChan := make(chan bool, 8)
    go writeData(intChan)
    for i := 0; i < 8; i++ {
        go judgData(intChan, resultChan, exitChan, wg.Done)
    }
    //go func() {
    //    for  i := 0 ; i < 8 ; i ++ {
    //    key  :=    <-exitChan
    //    fmt.Print(key)
    //    }
    //    close(resultChan)
    //}()
    wg.Add(8)
    wg.Wait()
    close(resultChan)
    func() {
        for i := 0; i < 8; i++ {
            key := <-exitChan
            fmt.Printf("key=%v\n", key)
        }
    }()
    for {
        res, ok := <-resultChan
        if !ok {
            break
        }
        fmt.Printf("素数=%d\n", res)
    }
}

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

740 次点击  
加入收藏 微博
9 回复  |  直到 2020-09-08 16:20:25
reckless-huang
reckless-huang · #1 · 5年之前

就很奇怪,协程的确都已经结束了,但是计算好像没有结束:screen

AdwindOne
AdwindOne · #2 · 5年之前

wg传入进去是值传递副本,要么取地址 要么闭包方式共用外部wg

reckless-huang
reckless-huang · #3 · 5年之前
AdwindOneAdwindOne #2 回复

wg传入进去是值传递副本,要么取地址 要么闭包方式共用外部wg

用管道阻塞实现了,这个打算先放一下

reckless-huang
reckless-huang · #4 · 5年之前
AdwindOneAdwindOne #2 回复

wg传入进去是值传递副本,要么取地址 要么闭包方式共用外部wg

谢谢您

lihongbin
lihongbin · #5 · 5年之前
// flag := true放到循环里面,为啥我也不知道
// resultChan <- num 写入判断逻辑放到内层for循环外面,这个原来的判断逻辑有问题
func judgData(intChan chan int, resultChan chan int, exitChan chan bool, done func()) {
    defer done()
    time.Sleep(time.Millisecond * 1000)
    //time.Sleep(time.Second *1)
    for {
        flag := true
        num, ok := <-intChan
        if !ok {
            fmt.Println("已从intChan读取所有数据")
            break
        }
        for i := 2; i < num; i++ {
            if num%i == 0 {
                flag = false
                break
            }
        }
        if flag {
            resultChan <- num
            fmt.Printf("添加%v到resultChan\n", num)
        }
    }
    fmt.Println("协程执行完毕")
    exitChan <- true
}
reckless-huang
reckless-huang · #6 · 5年之前

更新后的代码如下,可以正常工作,就是感觉不够优雅

package main

import (
    "fmt"
    _ "sync"
)

func writeData(intChan chan int) {
    for i := 1; i < 80; i++ {
        intChan <- i
    }
    fmt.Println("writeData()执行完毕")
    fmt.Println("关闭intChan")
    close(intChan)
}
func judgData(intChan chan int, resultChan chan int, exitChan chan bool) {
    //time.Sleep(time.Millisecond *100)
    //fmt.Println("开始读取intChan")
    flag := true
    for {
        flag = true //重置标志位,因为后面这个值被设置为false了
        num, ok := <-intChan
        if !ok {
            fmt.Println("数据读取完毕")
            break
        }
        for a := 2; a < num; a++ {
            if num%a == 0 {
                flag = false
                break
            }
        }
        if flag {
            resultChan <- num
        }
    }
    exitChan <- true
    fmt.Println("一个协程执行完毕")
}
func main() {
    intChan := make(chan int, 10000)
    resultChan := make(chan int, 10000)
    exitChan := make(chan bool, 8)
    //var wgw sync.WaitGroup
    //var wgr sync.WaitGroup
    //wgw.Add(1)
    //wgr.Add(8)
    go writeData(intChan)
    //go writeData(intChan, wgw.Done)
    //wgw.Wait()
    //close(intChan)
    for i := 0; i < 8; i++ {
        go judgData(intChan, resultChan, exitChan)
    }
    go func() {
        for sec := 0; sec < 8; sec++ {
            <-exitChan //如果没有值就会等待值写入,造成主线程的阻塞
        }
        close(resultChan)
    }() // 这里用协程是不让这个取值堵塞主线程的取值
    //time.Sleep(time.Second *10)
    //wgr.Wait()
    for {
        key, ok := <-resultChan
        if !ok { //这里会因为上面的协程没结束,没有关闭resultChan,ok的值一直未true而阻塞一直到所有协程执行完毕
            break
        }
        fmt.Printf("素数:%v\n", key)
    }
}
reckless-huang
reckless-huang · #7 · 5年之前

5楼 @lihongbin 谢谢您,判断那里的确有问题,flag = true 写在循环体是因为每次循环结束后flag的值被置为false了,要改回来,下面一楼有我改好的代码,您可以帮忙看看有可以优化的么

lihongbin
lihongbin · #8 · 5年之前

给两个小建议吧

1、关闭的channel可以用for range 来循环,所以最后打印素数的时候,可以写成

for key := range resultChan {
    fmt.Printf("素数:%v\n", key)
}

2、写代码的时候,最好业务跟逻辑分离处理,比如把判断是否是素数的方法拆出来

func judgData(intChan chan int, resultChan chan int, exitChan chan bool) {
    for {
        num, ok := <-intChan
        if !ok {
            fmt.Println("数据读取完毕")
            break
        }
        if isPrime(num) {
            resultChan <- num
        }
    }
    exitChan <- true
    fmt.Println("一个协程执行完毕")
}

func isPrime(n int) bool {
    if n < 2 {
        return false
    }

    for i := 2; i*i <= n; i++ {
        if n%i == 0 {
            return false
        }
    }
    return true
}

其他的就是一些个人习惯了,练习项目都无所谓

  • 比如多个相邻的变量声明,我喜欢用var()包起来
  • 比如多次用到的值,可以用常量代替
  • 比如开多个协程的时候,我习惯用WaitGroup或Context来处理
reckless-huang
reckless-huang · #9 · 5年之前
lihongbinlihongbin #8 回复

给两个小建议吧 1、关闭的channel可以用for range 来循环,所以最后打印素数的时候,可以写成 ```go for key := range resultChan { fmt.Printf("素数:%v\n", key) } ``` 2、写代码的时候,最好业务跟逻辑分离处理,比如把判断是否是素数的方法拆出来 ```go func judgData(intChan chan int, resultChan chan int, exitChan chan bool) { for { num, ok := <-intChan if !ok { fmt.Println("数据读取完毕") break } if isPrime(num) { resultChan <- num } } exitChan <- true fmt.Println("一个协程执行完毕") } func isPrime(n int) bool { if n < 2 { return false } for i := 2; i*i <= n; i++ { if n%i == 0 { return false } } return true } ``` 其他的就是一些个人习惯了,练习项目都无所谓 * 比如多个相邻的变量声明,我喜欢用var()包起来 * 比如多次用到的值,可以用常量代替 * 比如开多个协程的时候,我习惯用WaitGroup或Context来处理

学到了,谢谢您,还能请教您一个小问题么,我的一段代码

func (this *ExitMes) ExitMesProcess(mes *message.Message) (err error) {
    fmt.Println(mes)
    var exitMes  message.ExitMes
    err = json.Unmarshal([]byte(mes.Data), &exitMes)
    fmt.Println(err)
    data, err := json.Marshal(exitMes)
    fmt.Println(string(data))
    //println(*exitMes)
    if err != nil {
        err = model.ERROR_JSON_UNMARSHAL
        return
    }
    //根据userId将用户从在线列表中剔除
    userMgr.DelOnlineUser(exitMes.User.UserId)
    //将这条用户退出消息广播给其他在线用户
    sp := SmsProcess{}
    //println(mes)
    sp.SendGroupSms(mes)
    return
}

然而反序列的值无法写入到exitMes这个变量中

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传