go语言的map多协程访问时需要加锁吗

Python010

go语言的map多协程访问时需要加锁吗,第1张

go语言的map多协程访问时需要加锁

支持==和!=操作就可以做key,实际上只有function、map、slice三个kind不支持作为key,因为只能和nil比较不能和另一个值比较。布尔、整型、浮点、复数、字符串、指针、channel等都可以做key。

struct能不能做key要看每一个字段,如果所有字段都可以做key,那这个struct就可以。有一个字段不能做key,这个struct就不能做key。array也是,元素类型能做key,那这个array就可以。

例如:

type Foo map[struct {

B bool

I int

F float64

C complex128

S string

P *Foo

Ch chan Foo

}]bool

每一个字段都可以做key,Foo就可以做key。再如:

type Foo map[struct {

Fn func() Foo

M map[*Foo]int

S []Foo

}]bool

有一个字段不能做key、Foo就不允许做key,而这三个字段都不能。

字段是递归检查的:

type Foo map[struct {

Sub struct {

M map[*Foo]bool

}

}]bool

Sub的M字段不能做key,Sub就不能做key,Foo也就不能做key。

总之想把一个数据结构用于map的key,就不能包含function、map和slice。

上周从零学习了golang,语法简单关键字少,写个爬虫熟悉一下语法结构。

首先选用了原生的net/http包,基本上涵盖了所有的get/post请求,各种参数都可以设置,网上google到html页面解析goquery神器,很轻松就可以解决页面解析问题。

首先就写了个爬取汇率的爬虫。然后重写之前php的一个请求类,请求类的逻辑有点混乱不清晰,往往把两个不同的功能合并到一起写,粒度大,后来发现了一个好用的框架——colly,之后再试试好不好用

Windows 10 Golang

依赖包:goquery

较常用的方法有Find和Each

爬取中国银行的汇率牌价表,golang依赖net/http包和goquery包

唯一的难点是对于goquery方法的使用,需要阅读官方文档:

https://godoc.org/github.com/PuerkitoBio/goquery

使用原生的net/http包基本上可以解决大多数的网页请求,使用goquery可以解决页面解析问题

可以利用golang的协程特性进行异步多协程爬取

增加安全性可以通过几个方面进行改进:

1.首先可以限制爬虫的爬取速度

2.每次对网页的请求都随机选用一个客户端

3.选用IP代理池,防止IP误封(及限制ip访问次数)

构造请求代理ip网站的链接→获取网页内容→ 提取网页中IP地址和端口号→验证IP的有效性并存储

轻量级反爬虫方案

浅谈JSP

golang带json的Http请求

Get/Post

HTTP请求中的Form Data和Request Payload的区别

HTTP Json请求

https://zhuanlan.zhihu.com/p/32825491

https://blog.csdn.net/yang731227/article/details/83900422

net/http: https://studygolang.com/articles/9467

golang HTTP操作

python建立爬虫代理ip池

爬虫黑科技之让你的爬虫程序更像人类用户的行为

特点:事件监听,通过callback执行事件处理

基于colly开发的web管理界面

【译文】 原文地址

channel是Go语言的一个标志性特性,为go协程之间的数据交互提供一种非常强大的方式,而不需要使用锁机制。

本文将讨论channel的两个重要属性,一个是控制协程间数据发送和接收,以及对channel本身控制。

首先讨论下关闭的channel特性。一旦channel被关闭之后,就不能再继续发送数据给该channel,但是还是可以继续接收channel中的数据。如下所示:

output:

上述例子显示即使ch在for循环之前已经关闭,但还是可以正常的读取缓存中的true值,读完之后ok就会被赋值为false表示channel已经关闭,而且value值为对应channel类型bool的默认零值false。只要不停地从关闭的channel接收,就会无限的返回默认值和false。可以将for循环次数改大点试试即可验证。

通过以上例子可以发现,关闭的channel可以继续接收读取操作,这种特征是有用的。在使用range读取带缓存的channel时就会用到,一旦channel关闭,读取完缓存中数据就会停止接收数据退出。

将前面的例子改为如下:

output:

上面的例子就没有false打出来了。正好是写入channel里面的两个值。

channel与select结合更能发挥出其作用,让我们看一个例子:

上面的例子,因为finish在主协程中发送之后,马上就会在select中接收,并执行done.Done()。主协程wait马上会退出整个程序就结束。但是这里面存在一个问题,如果在select中没有添加finish case的话,主协程就永远发送不了数据到finish这个channel,因为其不带缓存。这里就可以通过将finish改成带缓存的channel,或者可以让select中的finish不会阻塞。

但是出现多个协程都在接收finish通道中的数据的话,就需要发送对应协程数量的值到channel中才能解决上面的问题。但是具体有多少个协程这往往是不好确定的,因为有些协程可能是程序其他部分创建的。一个比较好的选择就是通过使用关闭通道的方法来实现各协程能正常接收并结束。

如下所示:

output:

上面的例子就是使用了关闭的channel可以无限地接收到反馈数据。这样每个协程都能从finish通道中读到关闭信息并执行done.Done()使得主协程wait能退出。并且不需要关注多少个协程数,就能正确的让所有协程读到finish通道信息。

channel的这个特性,可以让程序员无需关注后台具体执行协程个数,确保每个协程都能接收到通道关闭信息,而无需担心死锁问题。

通过上面的例子我们也发现每个协程并不需要从通道中读取对应类型的数据,只需让接收操作能执行就行,让select不被阻塞。所以可以使用空结构体类型,我们可以改成如下:

这里我们只关注通道是否关闭这个信号,而不需要关注通道里面的数据,所以可使用空结构体类型通道。

第二个要讨论的是nil通道:如果定义了一个channel变量没有被初始化,或者被赋值为nil,那么该chennel总是处于阻塞状态。如下所示:

执行结果为:

因为channel为nil无法发送数据,当然也不能接收数据:

这个似乎看起来不是很重要,但是如果你想使用关闭channel来等待多个channel关闭的话,这个特性就有用处了。先看下面的例子:

WaitMany()函数看起来好像是一个等待通道a和b关闭的好方法,但是存在一个问题。假设a通道先关闭,case <-a就会变成非阻塞。因为bclosed还是false,程序就会进入到一个死循环当中,导致b通道永远无法确认关闭。

一个安全的方法就是使用nil通道总是阻塞的特点,如下所示:

上面的例子我们在WaitMany函数当中,当a或者b关闭时,case可执行了将对应的通道赋值为nil,让其阻塞这样就可以等待另一个通道关闭。当nil通道是select语句的一部分时,它会被有效地忽略,因此nil通道a会从select中删除它,只留下b,直到它被关闭,退出循环。

总之,closed和nil通道的简单属性对写出优质的go程序是很有用的,可以用来创建高并发程序。