Golang面试题目-程序设计题目(二)

题目一场景:某一高并发web服务器需限制IP的频繁访问,现模拟100个IP同时并发访问该服务器,每个IP需访问1000次。

题目一要求:每个IP三分钟内只能访问一次。

题目一内容:修改以下代码完成上述题目要求,要求能成功输出【success100】。

题目代码如下:

package mainimport (  "fmt"  "time")type Ban struct {  visitIPs map[string]time.Time}func NewBan() *Ban {  return &Ban{    visitIPs: make(map[string]time.Time),  }}func (o *Ban) visit(ip string) bool {  if _, ok := o.visitIPs[ip]; ok {    return true  }  o.visitIPs[ip] = time.Now()  return false}func main() {  success := 0  ban := NewBan()  for i := 0; i < 1000; i++ {    for j := 0; j < 100; j++ {      go func() {        ip := fmt.Sprintf("192.168.1.%d", j)        if !ban.visit(ip) {          success++        }      }()    }  }  fmt.Println("success:", success)}

解析:上述问题主要考察的是并发情况下map的读写问题,上述案例代码中又存在for循环中启动goroutine时变量使用问题以及goroutine执行滞后问题。

我们首先要做的就是保证启动的goroutine能得到正常的参数,然后就是保证map的并发读写,左后保证三分钟之内只能访问一次。

多CPU核心下修改int的值极端情况下会存在不同步的情况,一次需要原子性的修改int值,来看下代码示例:

package mainimport (  "context"  "fmt"  "sync"  "time")type Ban struct {  visitIPs map[string]time.Time  lock     sync.Mutex}func NewBan(ctx context.Context) *Ban {  o := &Ban{visitIPs: make(map[string]time.Time)}  go func() {    timer := time.NewTimer(time.Minute * 1)    for {      select {      case <-timer.C:        o.lock.Lock()        for k, v := range o.visitIPs {          if time.Now().Sub(v) >= time.Minute*1 {            delete(o.visitIPs, k)          }        }        o.lock.Unlock()        timer.Reset(time.Minute * 1)      case <-ctx.Done():        return      }    }  }()  return o}func (o *Ban) visit(ip string) bool {  o.lock.Lock()  defer o.lock.Unlock()  if _, ok := o.visitIPs[ip]; ok {    return true  }  o.visitIPs[ip] = time.Now()  return false}func main() {  success := int64(0)  ctx, cancel := context.WithCancel(context.Background())  defer cancel()  ban := NewBan(ctx)  wait := &sync.WaitGroup{}  wait.Add(1000 * 100)  for i := 0; i < 1000; i++ {    for j := 0; j < 100; j++ {      go func(num int) {        defer wait.Done()        ip := fmt.Sprintf("192.168.1.%d", num)        if !ban.visit(ip) {          success++        }      }(j)    }  }  wait.Wait()  fmt.Println("success:", success)}

上述代码启动了一个协程每分钟查验一下map中过期的ip,for循环在启动goroutine时传参也做了优化。

题目二:写出以下逻辑,要求每秒钟调用一次proc并保证程序不退出。

package mainfunc main() {  go func() {    // 1 在这里需要你写算法    // 2 要求每秒钟调用一次proc函数    // 3 要求程序不能退出  }()  select {}}func proc() {  panic("ok")}

解析:

  上述题目中考察知识点如下:

  1. 定时执行任务

  2. 捕获panic错误

为实现题目中每秒执行一次的要求,我们使用time.Ticker对象,这个函数可以每秒钟往chan中放一个Time,符合预期,之后使用recover()函数来捕获panic异常。

代码案例如下:

package mainimport (  "fmt"  "time")func main() {  go func() {    // 1 在这里需要你写算法    // 2 要求每秒钟调用一次proc函数    // 3 要求程序不能退出    t := time.NewTicker(time.Second * 1)    for {      select {      case <-t.C:        go func() {          defer func() {            if err := recover(); err != nil {              fmt.Println(err)            }          }()        }()        proc()      }    }  }()  select {}}func proc() {  panic("ok")}

题目三:使sync.WaitGroup中的Wait函数支持WaitTimeout功能。

package mainimport (  "fmt"  "sync"  "time")func main() {  wg := sync.WaitGroup{}  c := make(chan struct{})  for i := 0; i < 10; i++ {    wg.Add(1)    go func(num int, close <-chan struct{}) {      defer wg.Done()      <-close      fmt.Println(num)    }(i, c)  }  if WaitTimeout(&wg, time.Second*5) {    close(c)    fmt.Println("timeout exit")  }  time.Sleep(time.Second * 10)}func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {  // 要求手写代码  // 要求sync.WaitGroup支持timeout功能  // 如果timeout到了超时时间返回true  // 如果WaitGroup自然结束返回false}

解析:本身sync.WaitGroup对象中的Wait函数就是阻塞的,同时,超时用到的time.Timer对象也要阻塞读。

同时阻塞两个对象肯定要对应每一个对象启动一个协程,每个协程去处理一个阻塞,难点就在于如何知道那个阻塞先完成。

可以声明一个无缓冲的chan,那个协程的阻塞先完成就先往里写入数据。

代码案例如下:

package mainimport (  "fmt"  "sync"  "time")func main() {  wg := sync.WaitGroup{}  c := make(chan struct{})  for i := 0; i < 10; i++ {    wg.Add(1)    go func(num int, close <-chan struct{}) {      defer wg.Done()      <-close      fmt.Println(num)    }(i, c)  }  if WaitTimeout(&wg, time.Second*5) {    close(c)    fmt.Println("timeout exit")  }  time.Sleep(time.Second * 10)}func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {  // 要求手写代码  // 要求sync.WaitGroup支持timeout功能  // 如果timeout到了超时时间返回true  // 如果WaitGroup自然结束返回false  ch := make(chan bool, 1)  go time.AfterFunc(timeout, func() {    ch <- true  })  go func() {    wg.Wait()    ch <- false  }()  return <-ch}

扫码关注公众号,获取更多优质内容。

  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luyaran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值