本文共 4821 字,大约阅读时间需要 16 分钟。
【实例简介】
C#截取屏幕源码 可直接截屏、截取当前窗口 实例下载
【实例截图】
private CopyScreen s;
public Form1() { InitializeComponent(); //try //{ // s = new CopyScreen(); // s.GetScreenImage = new CopyScreen.GetImage(s_GetScreenImage); //} //catch (Exception ex) { MessageBox.Show(ex.Message); } }void s_GetScreenImage(Image p_Image)
{ pictureBox1.Image = p_Image; } private void button3_Click(object sender, EventArgs e) { //s.GerScreenFormRectangle(); MessageBox.Show(“未实现”); } private void button1_Click(object sender, EventArgs e) { Bitmap myImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); Graphics g = Graphics.FromImage(myImage); g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)); IntPtr dc1 = g.GetHdc(); g.ReleaseHdc(dc1); myImage.Save(@“c:\screen0.jpg”); }private void button2_Click(object sender, EventArgs e)
{ Bitmap myImage = new Bitmap(this.Width, this.Height); Graphics g = Graphics.FromImage(myImage); g.CopyFromScreen(new Point(this.Location.X, this.Location.Y), new Point(0, 0), new Size(this.Width, this.Height)); IntPtr dc1 = g.GetHdc(); g.ReleaseHdc(dc1); myImage.Save(@“c:\screen1.jpg”); }以下内容无关:
-------------------------------------------分割线---------------------------------------------
go-cache 过期清理方案
func (c *cache) DeleteExpired() {
log.Printf(“start check at:%v”, time.Now()) var evictedItems []keyAndValue now := time.Now().UnixNano() c.mu.Lock() for k, v := range c.items { // “Inlining” of expired if v.Expiration > 0 && now > v.Expiration { ov, evicted := c.delete(k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) } } } c.mu.Unlock() for _, v := range evictedItems { c.onEvicted(v.key, v.value) } }func (j *janitor) Run(c *cache) {
ticker := time.NewTicker(j.Interval) for { select { case <-ticker.C: c.DeleteExpired() case <-j.stop: ticker.Stop() return } } }func runJanitor(c *cache, ci time.Duration) {
j := &janitor{ Interval: ci, stop: make(chan bool), } c.janitor = j go j.Run© }func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {
c := newCache(de, m) // This trick ensures that the janitor goroutine (which–granted it // was enabled–is running DeleteExpired on c forever) does not keep // the returned C object from being garbage collected. When it is // garbage collected, the finalizer stops the janitor goroutine, after // which c can be collected. C := &Cache{c} if ci > 0 { runJanitor(c, ci) runtime.SetFinalizer(C, stopJanitor) } return C } 可以看到 go-cache 每过一段时间(j.Interval, 这个值也是通过 go-cache.New 设置), 就会启动清理工作.清理时原理:
RWMutex.Lock()
遍历整个map, 检查 map 中的 value 是否过期 RWMutex.Unlock() 同时, 还利用了 runtime.SetFinalizer 在 go-cache 生命周期结束时, 主动完成对过期清理协程的终止源码分析总结
遍览整个 go-cache 源码, 会发现 go-cache 完全靠着 RWMutex 保证数据的正确性.考虑下面的问题:
当 go-cache.New() 时设置的定时清理的时间过长, 同时 Set 的 key 的过期时间比较长, 这样会不会导致 go-cache.map 中的元素过多?
会不会当清理启动时, 锁定了 go-cache.map (注意这个时候是写锁), 由于 go-cache.map 中元素过多, 导致 map 一直被锁定, 那么这个时候所有的 Set 函数是不是就会产生 Lock 竞争? 使用 go-cache 的时候, 当某个接口的 QPS 很高, 程序里由于使用问题, 将某些不该往 go-cache 存的 value 也存了进去, 那么会不会导致 Set 之间的 Lock 竞争呢? 场景还原 利用下面的程序可以轻松还原上面的问题场景. 上面提出的问题, 都会造成 go-cache lock 竞争. 这里利用 pprof 查看程序的指标var goroutineNums = flag.Int(“gn”, 2, “goroutine nums”)
func main() { flag.Parse() go func() { log.Println(http.ListenAndServe(“localhost:6060”, nil)) }()rand.Seed(time.Now().Unix())lc := cache.New(time.Minute*5, time.Minute*2)log.Printf("start at:%v", time.Now())aaaKey := "aaa:%d:buy:cnt"log.Println("set run over")for i := 0; i < *goroutineNums; i++ { go func(idx int) { for { key := fmt.Sprintf(aaaKey, rand.Int()) newKey := fmt.Sprintf("%s:%d", key, rand.Int()) v := rand.Int() lc.Set(newKey, v, time.Millisecond) } }(i)}// 查看 go-cache 中 key 的数量go func() { ticker := time.NewTicker(time.Second) for { select { case <-ticker.C: log.Printf("lc size:%d", lc.ItemCount()) } }}()select {}
}
模拟接口高QPS ./go-cache-test -gn 2000 2020/03/12 00:32:33 start at:2020-03-12 00:32:33.949073 +0800 CST m=+0.001343027 2020/03/12 00:32:34 lc size:538398 2020/03/12 00:32:35 lc size:1149109 high-qps瞬间就会出现锁竞争
模拟 go-cache 启动清理时的情形
./go-cache-test -gn 2 2020/03/12 00:37:33 start at:2020-03-12 00:37:33.171238 +0800 CST m=+0.001457393 … 2020/03/12 00:40:35 lc size:54750220 2020/03/12 00:40:35 start clear at:2020-03-12 00:40:35.103586 +0800 CST m=+120.005547323 2020/03/12 00:41:51 lc size:33 2020/03/12 00:41:51 lc size:50 clear会看到当清理 map 的时候, 如果 map 中的数据过多就会造成 Lock 竞争, 造成其他数据无法写入 map
总结
我使用的问题 背景: 某接口 QPS 有点高当时考虑到用户购买状态(这个状态可能随时变化)如果能够在本地缓存中缓存 10s, 那么用户再次点进来的时候能从本地取了, 就造成大量的数据都写入了 map 中
由于接口 QPS 比较高, 设置用户购买状态时就可能造成竞争, 造成接口响应超时 go-cache 使用注意点 尽量存放那些相对不怎么变化的数据, 适用于所有的 local cache(包括 map, sync.map) go-cache 的过期检查时间要设置相对较小, 也不能过小 那些高 QPS 的接口尽量不要去直接 Set 数据, 如果必须 Set 可以采用异步操作 监控 go-cache 里面 key 的数量, 如果过多时, 需要及时调整参数转载地址:http://tetyk.baihongyu.com/