本帖最后由 523066680 于 2021-4-27 14:09 编辑
多线程抓取网络数据的场景,耗时更多的仍然是网络部分。
即使是一个序列化数组(串行操作),若只是在push, shift操作的前后加锁和解锁(而不是在网络请求阶段就开始锁),占用的时间其实微不足道。
对于高性能并行运算的数据共享,不太了解。GPU Shader 的运算是可以并行处理最后直接输出值的,但是场景特殊。
比如10个线程,首先处理前10个坑,若其中一个线程先处理完了,它立即去处理第11个坑,已处理完的数据就留在坑里,这样直到所有的坑处理完为止
之前做过类似的做法,开10个常驻线程,他们从一个共享数组中获取任务并输出结果。也分为两种方式
1. 一开始就为线程分配等量的任务,不过有的线程会先处理完所有任务,提前进入空闲或者detach
2. 每个线程共享一个index索引,谁执行完了,就领取下一个任务。为了避免$index计数冲突, 依然要用到变量锁
后来改用队列
Thread::Queue 包为线程提供了线程安全的队列支持。与信号量类似,从内部实现上看,Thread::Queue 也是把一个通过锁机制实现同步访问的共享队列封装成了一个线程安全的包,并提供统一的使用接口。Thread::Queue 在某些情况下可以大大简化线程间通信的难度和成本。例如在生产者 - 消费者模型中,生产者可以不断地在线程队列上做 enqueue 操作,而消费者只需要不断地在线程队列上做 dequeue 操作,这就很简单地实现了生产者和消费者之间同步的问题。例如
再后来用上了 Mojolicious,省心- # Non-blocking request
- $ua->get('mojolicious.org' => sub ($ua, $tx) { say $tx->result->dom->at('title')->text });
- Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
复制代码
- my $wdir = "D:/blah";
- for my $city ( keys %list )
- {
- my $name = sprintf "%s/%s.json", $wdir, gbk($city);
- $query->{"toCountryId"} = $ct_code->{$city}; # 城市ID 更新到请求数据中
- $res = $ua->post( $url, to_json( $query ), closure->( $name ) );
- }
-
- $loop->start unless $loop->is_running;
-
- sub closure ($file) {
- return sub ($ua, $tx) {
- printf "%s\n", $file;
- write_file( $file, $tx->result->body );
- }
- }
复制代码
|