现在有个需求:
用户输入图片URL,首先本地检测图片是否存在,如果存在,则直接输出,如果不存在则下载然后保存。
问:当多个线程执行这步的时候,如果保证只有一个线程在下载?并且其他线程可以在存储线程执行完成后执行输出操作?
锁的话是用不了的,因为一锁的话,几乎所有的请求都在等待了。
我现在的做法是使用一个HashSet来存储,每次下载的时候先检测HashSet中是否存在,如果存在,就等待10秒,然后重新走一遍。
不太明白你的实现,如果单独考虑单进程的玩意,你那HashSet不就可以做锁了么?(最好换成字典,key为url或者图片唯一标识,value为超时时间)
按照字典实现ConcurrentDictionary<string,DateTime>,每次在执行到这里时首先判断字典中是否存在对应key,如果有则检查其value是否过期,如果过期则更新为当前时间+过期时间,同时更改上下文标识表示我可以开始下载及保存逻辑;反之则更改代码上下文标识已表示其他线程已经在操作,然后重复上述逻辑(前几句描述为过程,实现核心为ConcurrentDictionary#AddOrUpdate方法)。
如果考虑多进程场景则考虑将这个锁移到第三方组件中,如zookeeper或redis中(建议redis,因为其执行过程单线程确保数据线程安全性,但由于整个逻辑牵扯步骤过多,需要使用lua脚本加载到redis中单线程执行)
你好,我现在的做法 是把 URL当作了一个key存储到了ConcurrentDictionary里面,已经可以实现了这个业务需求了,后期如果涉及到分布式的话,我会把ConcurrentDictionary换成redis来做.
@大壮他哥:
1.确保你的value中包含超时信息,因为这里可能无法预料获取“锁”的线程会出现什么情况,同时也为了保证其他线程不至于等待时间过久影响体验
2.ConcurrentDictionary几个原子性方法要小心点,确保其后面的委托方法为惰性(之前我就掉过这个坑)
下载下来往一个内存缓存里放.
再起一个独立线程.从缓存里拿数据往磁盘写
谢谢您的回复,这种方案我先尝试一下。