线程安全
在高并发任务的时候,经常会遇到安全性问题,也就是对变量进行不符合预期的修改。
从小处说,一个数据库字段,可能被几个同时并发的任务修改。如:爬虫的爬取状态(开始/成功/失败)
此时可以改变之前加锁,别的线程就不能访问了,改完之后解锁。
看具体的使用场景,给任务方法返回“等待”或者“失败”。
有 n 种方式来实现这个锁的机制,只要逻辑是通的就好了。
比如根据地址爬取餐厅,我可以将地址的ID作为key存入一个值到redis,
当并发来的时候查看这个key有没有值,有的话就返回正在爬取。
从大处说,维护一个动态的有机的ip池。
如果写成机械的不加判断的补充,就会因并发引起同时补充n倍目标数量的ip。
即使加了判断当前ip池数量,也会因几个任务同时读取到补充前的数值作为参数,而在任务陆续执行完毕后,发现补充结果超出期望。
想要解决这个问题,必须控制代码的颗粒度和判断逻辑,如判断谁,何时判断。
比如,一个补充任务,先向代理ip网站请求一批ip,这个请求过程可能会耗时,而我们对ip池的上限是固定的,那么我们就应把IP池上限作为目标值,把ip池数量和上限的差值作为参数。在向代理商拿到一批ip之后,向ip池添加之前判断:现在的ip池数量与上限是否存在差值,以此来作为是否添加的依据。而不是在任务一开始、还没有向代理商拿ip的时候判断,是否添加。
在这个例子中,颗粒度是由时差决定的,而判断逻辑安插在哪儿是由颗粒度决定的。
请求的时差大于修改本地变量的时差,所以判断要在离修改本地变量最近的地方。