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时要加锁。

 

 

 

你可能感兴趣的:(golang基础)