golang 应用协程和channel实现高并发
问题:模拟100个ip向server发送请求,每个ip重复请求1000次,间隔时间不得超过3s。
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
const IP_NUMBER = 100
type ipTestTask struct {
handleOk chan struct{}
idle bool
}
type Ban struct {
IPs [IP_NUMBER]ipTestTask
}
func NewBan() *Ban {
var o Ban
for i := 0; i < IP_NUMBER; i++ {
o.IPs[i].handleOk = make(chan struct{})
o.IPs[i].idle = true
}
return &o
}
func (o *Ban) addTask(i int) {
o.IPs[i].idle = false
//to do task
go o.invalidAfter3Min(i)
}
func (o *Ban) wait(i int) struct{} {
fmt.Println("wait to read")
return <-o.IPs[i].handleOk
}
func (o *Ban) isBuzy(i int) bool {
if !o.IPs[i].idle {
return true
}
return false
}
// 3s后ip失效, ip再次访问时便可正常访问
func (o *Ban) invalidAfter3Min(i int) {
time.Sleep(time.Second * 3)
o.IPs[i].idle = false
o.IPs[i].handleOk <- struct{}{}
}
func main() {
var success int64
ban := NewBan()
wg := new(sync.WaitGroup)
for i := 0; i < IP_NUMBER; i++ {
ip := fmt.Sprintf("192.168.1.%d", i)
for j := 0; j < 1000; j++ {
wg.Add(1)
go func(i int, ip string, j int) {
defer wg.Done()
if ban.isBuzy(i) {
ban.wait(i)
}
ban.addTask(i)
atomic.AddInt64(&success, 1)
fmt.Println("add success", ip, "// ", j)
}(i, ip, j)
}
}
wg.Wait()
fmt.Println("success:dddd", success)
}
优化:考虑是否可以去掉idle标志符,直接使用channel实现状态的判断。
结果:直接在添加任务时读channel 如果能读到数据,说明3s间隔已解禁,ip可以继续访问,否则阻塞。不需要判断idle值也可以判断是否需要等待。不过采用这种思路需要在初始化channel之后向每个channel内写入数据,保证第一个添加任务时读channel成功。
func NewBan() *Ban {
var o Ban
for i := 0; i < IP_NUMBER; i++ {
o.IPs[i].handleOk = make(chan struct{})
o.IPs[i].idle = true
}
go func(o *Ban) {
for i := 0; i < 100; i++ {
o.IPs[i].handleOk <- struct{}{}
}
}(&o)
return &o
}
func (o *Ban) addTask(i int) {
//o.IPs[i].idle = false
//to do task
<-o.IPs[i].handleOk
go o.invalidAfter3Min(i)
}
总结:在ip随机的情况下,可以采用map。多协程操作map时要加锁。
版权声明:本文为bubumai原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。