C++代码利用pthread线程池与curl批量下载地图瓦片数据
项目需求编写的程序,稳定性有待进一步测试。
适用场景:在网络地图上,比如天地图与谷歌地图,用户用鼠标在地图上拉一个矩形框,希望下载该矩形框内某一层级的瓦片数据,并将所有瓦片拼接成一个完整的,包含地理坐标的tif图像。
之前利用gdal2tiles.py中的代码改写成c++版本,实现了网格瓦片的计算,获得了矩形范围内某一具体层级所包含的所有瓦片的网络请求地址。
见:http://www.cnblogs.com/akaishi/p/7418799.html
那么用c++编写的后端代码如何能快速、大批量下载瓦片图像?
下载使用curl库,地址:https://curl.haxx.se/download.html
加速的办法先是尝试了OpenMP,加速效果有限,优化了几天1千幅瓦片的下载速度还是超过1分钟。
为了进一步提高下载速度,打算从更基础的多线程pthead库编写下载代码。
网上查找了并尝试了几个线程池的代码实现,速度差别还是浮动挺大的,最后采用的一个简单的实现,代码长度短,速度也理想,就是忘记从哪里找的了,这里贴出来吧:
头文件:
#ifndef __THREAD_POOL_H__ 
#define __THREAD_POOL_H__
#include <stdio.h>  
#include <pthread.h>  
#include <assert.h>  
#include <iostream>  
#include <list>
using namespace std;
class CJob{
public:
	CJob(void* (*r)(void* arg), void* a) : callback_routine(r), arg(a){}
	~CJob(){}
void* (*callback_routine) (void* arg);
	void* arg;
};
//fixed size thread pool  
class CThreadPool{
public:
	CThreadPool(int max_th_num);
	~CThreadPool();
int pool_add_job(void* (*process)(void* arg), void* arg);
pthread_mutex_t      queue_mutex;
	pthread_cond_t       queue_cond;
	list<CJob*>          queue_job;
	pthread_t*           thread_vec;
	int                  max_thread_num;
	int                  cur_queue_size;
	int                  shutdown;
};
#endif
cpp文件:
#include "thread_pool.h"
static void* thread_routine(void* arg){
	CThreadPool* pool = (CThreadPool*)arg;
	if (pool == NULL) return NULL;
while (1){
		pthread_mutex_lock(&pool->queue_mutex);
		while (pool->cur_queue_size == 0 && !pool->shutdown){
			pthread_cond_wait(&pool->queue_cond, &pool->queue_mutex);
		}
if (pool->shutdown){
			pthread_mutex_unlock(&pool->queue_mutex);
			pthread_exit(NULL);
		}
assert(pool->cur_queue_size != 0);
		assert(!pool->queue_job.empty());
pool->cur_queue_size--;
		CJob* job = pool->queue_job.front();
		pool->queue_job.pop_front();
		pthread_mutex_unlock(&pool->queue_mutex);
(*job->callback_routine) (job->arg);
		delete job;
	}
}
CThreadPool::CThreadPool(int max_th_num) : cur_queue_size(0), shutdown(0), max_thread_num(max_th_num){
	pthread_mutex_init(&queue_mutex, NULL);
	pthread_cond_init(&queue_cond, NULL);
	thread_vec = new pthread_t[max_thread_num];
	for (int i = 0; i < max_thread_num; i++){
		pthread_create(&thread_vec[i], NULL, thread_routine, (void*)this);
	}
}
CThreadPool::~CThreadPool(){
	if (shutdown) return;
	shutdown = 1;
	pthread_cond_broadcast(&queue_cond);
	for (int i = 0; i < max_thread_num; i++)
		pthread_join(thread_vec[i], NULL);
	delete[] thread_vec;
	for (list<CJob*>::iterator it = queue_job.begin(); it != queue_job.end(); ++it)
		delete *it;
	queue_job.clear();
	pthread_mutex_destroy(&queue_mutex);
	pthread_cond_destroy(&queue_cond);
}
int CThreadPool::pool_add_job(void* (*process)(void* arg), void* arg){
	CJob* job = new CJob(process, arg);
pthread_mutex_lock(&queue_mutex);
	queue_job.push_back(job);
	cur_queue_size++;
	pthread_mutex_unlock(&queue_mutex);
	pthread_cond_signal(&queue_cond);
	return 0;
	}
利用curl下载单个瓦片图像的函数实现为:
void* job_process(void* ar)
 {           //huCurl_3对应原来的job_process
	CURL* curl;
	CURLcode res;
	JobInfo* ji = (JobInfo*)ar;
	FILE* fp;
	string full_path = ji->downfile;
	fp = fopen(full_path.c_str(), "wb"); //在此统一打开文件
curl = curl_easy_init();
	//curl_easy_setopt(curl, CURLOPT_FILE, );
	curl_easy_setopt(curl, CURLOPT_URL, ji->url.c_str());
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3L);
	curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
	curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
	curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 30L);
	curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0"); //user-agent  
	curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L);
res = curl_easy_perform(curl);
	if (CURLE_OK != res){
		printf("单个图像下载错误:[%d] %s\n", res, ji->url.c_str());
	}
	fclose(fp);
curl_easy_cleanup(curl);
//TODO: 回馈下载分片的完成情况....  
	Sleep(1);
return;
}
利用线程池下载函数为:
vector<JobInfo> ji_vec;
	 get_job_queue2(filesUrl, filesDown, ji_vec);
int num = ji_vec.size();//用来记录下载瓦片的个数
for (int i=0;i<num;i++)
	 {
		 //cout<<i<<":"<<num<<endl;
		 pool->pool_add_job(job_process, (void*)&ji_vec[i]);
	 }
// 下载等待结束
	 int waitnum = 0;
	 while(!check4done(filesDown))
	 {
		 waitnum = waitnum + 1;
		 cout<<"等待:"<<waitnum<<endl;
		 if (waitnum>10)
		 {
			 LogHandler("HuTilesMosaic.log", "网络拥堵?等待...");
		 }
		 if (waitnum>100)
		 {
			 LogHandler("HuTilesMosaic.log", "下载失败!");
			 delete pool;
			 return 1;
		 }
		 Sleep(10000);
	 }
delete pool;
该实现下载谷歌图像,在教育网垃圾网速下,1万幅瓦片下载时间1分钟以内,这比用简单的openMP快了不少!
该实现方式存在的问题主要是所有线程结束时的反馈得不到,这里采用的笨办法是隔5秒检查文件夹下下载的瓦片数量是否达到总数,没有下载完则再等5秒。。。
所以有谁能通过上述代码找到办法?将所有线程结束后join返回给主线程?
另外,目前下载的瓦片图像是保存在本地文件夹下的,然后再利用gdal进行图像镶嵌。这样硬盘反复读写的时间消耗便不能忽略。
所以下一步的优化是内存操作,curl下载到标准输出,gdal直接读取内存中的瓦片文件,再保存到硬盘的镶嵌结果tif图像中。
所以如何获得线程结束返回啊???再搞两天等有了结果再说吧。
目前下载支持EPSG4326经纬度与EPSG3857谷歌全球墨卡托投影。经纬度瓦片切图规则与天地图相同,从第一层开始切,第一层包含两个瓦片。谷歌全球墨卡托从第0层开始切,第0层一个瓦片。
C++代码利用pthread线程池与curl批量下载地图瓦片数据的更多相关文章
- [改善Java代码]优先选择线程池
		在Java1.5之前,实现多线程编程比较麻烦,需要自己启动线程,并关注同步资源,防止线程死锁等问题,在1.5版本之后引入了并行计算框架,大大简化了多线程开发. 我们知道线程有5个状态:新建状态(New ... 
- Linux pthread 线程池实现
		基于pthread封装了一个简易的ThreadPool,具有以下特性: 1.具有优先级的任务队列 2.线程池大小可以二次调整,增加线程或者删除空闲线程 3.任务两种重写方式,重写run或者使用函数回调 ... 
- 利用 Windows 线程池定制的 4 种方式完成任务(Windows 核心编程)
		Windows 线程池 说起底层的线程操作一般都不会陌生,Windows 提供了 CreateThread 函数来创建线程,为了同步线程的操作,Windows 提供了事件内核对象.互斥量内核对象.关键 ... 
- 在C#应用程序中,利用表值参数过滤重复,批量向数据库导入数据,并且返回重复数据
		在很多情况下,应用程序都需要实现excel数据导入功能,数据如果只有几十条,或上百条,甚至上千条,速度还好. 但是不仅如此,如果客户提供给你的excel本身存在着重复数据,或是excel中的某些数据已 ... 
- 利用斗图啦网站API批量下载表情图片
		decorator.py #!/usr/bin/env python # -*- coding: utf-8 -*- import logging import os from functools i ... 
- .net重启iis线程池和iis站点程序代码【转】
		转:http://www.jb51.net/article/44162.htm 重启站点: 复制代码代码如下: /// <summary> /// 根据名字重启站点.(没重 ... 
- 线程系列06,通过CLR代码查看线程池及其线程
		在"线程系列04,传递数据给线程,线程命名,线程异常处理,线程池"中,我们已经知道,每个进程都有一个线程池.可以通过TPL,ThreadPool.QueueUserWorkItem ... 
- Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析
		Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThread ... 
- 线程池(C#)
		转自:http://blog.sina.com.cn/s/blog_494305f30100ryw7.html 在这里你可以学到Microsoft研究CLR实现线程池的原理机制,从而更灵活的处理CLR ... 
随机推荐
- C#连接mysql数据库的一个例子和获取本机IP的方法
			本例子是一个最初级直接连接mysql数据库的例子,实现了往数据库插入数据的操作: string MyConnectionMysql="Server=localhost;Datbase=xxx ... 
- Ocelot简易教程(四)之请求聚合以及服务发现
			上篇文章给大家讲解了Ocelot的一些特性并对路由进行了详细的介绍,今天呢就大家一起来学习下Ocelot的请求聚合以及服务发现功能.希望能对大家有所帮助. 作者:依乐祝 原文地址:https://ww ... 
- Redis(1)---五种数据结构
			五种数据结构 一.全局key操作 --删 flushdb --清空当前选择的数据库 del mykey mykey2 --删除了两个 Keys --改 --将当前数据库中的 mysetkey 键移入到 ... 
- Workspace in use or cannot be created, choose a different one.错误的解决办法
			eclipse 或 myeclipse 使用一段时间后,有时会因为一些故障自己就莫名奇妙的关闭了,再打开时有时没有问题,有时有会提示错误: Workspace Unavailable: Workspa ... 
- java 中文乱码的解决方法
			1. 这方法行之有效,但是谨慎用,它会作用服务器.超链接中带有的中文字符,<a class="add" href = "system/showDataAdd.act ... 
- eclipse连接github,链接不上 cannot open git-upload-pack(git-receive-pack)
			2018年2月8日后禁止通过TLSv1.1协议连接https://github.com 和 https://api.github.com. 原文地址为https://githubengineering ... 
- [深度学习] 权重初始化--Weight Initialization
			深度学习中的weight initialization对模型收敛速度和模型质量有重要影响! 在ReLU activation function中推荐使用Xavier Initialization的变种 ... 
- 基于vue与vux做的可滑动tab组件(附源码)
			背景 前不久,刚完成了一个商品列表+购物车功能的页面,因为一级商品分类在顶部tab中显示,可滑动,间距可定制,如下图所示: 定制的tab需求如下: 1. 每个tab-item的间距是相同的,可定制 2 ... 
- Perl语法的基本规则
			因为是比较凌乱的用法规则收集,所以能看懂则看,不能看懂也无所谓.以后也会遇到. Perl脚本第一行使用#!.Perl的后缀名一般为".plx"或".pl",运行 ... 
- Go Web:URLs
			URL也是一个结构体: type URL struct { Scheme string Opaque string // encoded opaque data User *Userinfo // u ... 
