zoukankan      html  css  js  c++  java
  • 开发 Windows 8 Bing地图应用(6)

    6)计算最佳路线。执行线路计算的核心算法定义在AntSystem.h和AntSystem.cpp中,这些文件定义了AntSystem命名空间,这个命名空间不包含WinRT上的依附元素,所以并不使用C++/CX。AntSystem.h定义了LatLong,Node和Edge结构体,同时也定义了OptimizeRoute函数。

    LatLong结构体表示地图上一个点的经纬度,代码如下。

    struct LatLong

    {

    explicit LatLong(double latitude, double longitude)

    : Latitude(latitude)

    , Longitude(longitude)

    {

    }

    // 位置坐标

    double Latitude;

    double Longitude;

    };

    Node结构体表示图像中一个节点,包含位置的名称,经纬度,也含从Bing Maps服务中而来的替换名称,代码如下。

    struct Node

    {

    explicit Node(const std::wstring& inputName)

    : InputName(inputName)

    , ResolvedLocation(0.0, 0.0)

    {

    }

    // 用户提供的位置名称

    std::wstring InputName;

    // Bing Maps位置服务提供的位置经纬度

    LatLong ResolvedLocation;

    std::wstring ResolvedName;

    //

    //匹配当前输入所有可能位置的平行数组(数组中包含名称字符串,经纬度等)

    //比如在输入一个城市名称后,显示一系列和此城市名相近的(匹配的)具体位置名称。

    // 位置数组会包含对应的每个位置经纬度

    std::vector<std::wstring> Names;

    std::vector<LatLong> Locations;

    };

    Edge结构体连接两个节点,并包含点间距离,同样也包含用作蚁群优化算法的数据,代码如下。

    struct Edge

    {

    explicit Edge(std::shared_ptr<Node> pointA, std::shared_ptr<Node> pointB)

    : PointA(pointA)

    , PointB(pointB)

    , Pheromone(0.0)

    , TravelDistance(-1.0)

    {

    }

    // 开始节点

    std::shared_ptr<Node> PointA;

    // 结束节点

    std::shared_ptr<Node> PointB;

    //棱边的信息数

    double Pheromone;

    // 始末节点的距离

    double TravelDistance;

    };

    C++组件为每个线路中的位置创建一个Node对象,为每对位置间创建一个Edge对象。在组件集合了所有从Bing Maps web服务上获取的必要信息后,它就会调用OptimizeRoute计算最佳路线,代码如下。

    // 计算所给出节点间距离,算出最短距离

    // 这个方法提供Node集合

    std::vector<size_t> OptimizeRoute(

    std::vector<std::shared_ptr<Node>>& nodes,

    std::vector<std::shared_ptr<Edge>>& edges,

    double alpha,

    double beta,

    double rho,

    unsigned int iterations,

    Concurrency::cancellation_token cancellationToken,

    std::function<void(unsigned int)>* progressCallback = nullptr,

    bool parallel = true);

    算法使用的一个重要方面是利用并发性,蚁群优化算法执行3个基本步骤的几次迭代,代码如下。

    // 执行几次蚁群算法模拟

    auto startTime = GetTickCount64();

    for (unsigned int i = 0; i < iterations; ++i)

    {

    // 不定期检查是否取消

    auto time = GetTickCount64();

    if (time - startTime > 100) {

    if (cancellationToken.is_canceled()) {

    // 返回空集合

    return vector<size_t>();

    }

    startTime = time;

    }

    // 发送进度

    if (progressCallback != nullptr) {

    (*progressCallback)(i);

    }

    // 注意这个操作可并行执行

    // 这个步骤不包含共享数据或依附性计算

    if (parallel) {

    parallel_for_each(begin(ants), end(ants), [&](Ant& blitz) {

    blitz.Explore();

    });

    }

    else {

    for_each(begin(ants), end(ants), [&](Ant& blitz) {

    blitz.Explore();

    });

    }

    //

    for_each(begin(edges), end(edges), [rho](shared_ptr<Edge> edge) {

    edge->Pheromone *= (1.0 - rho);

    });

    //图像中回溯

    // 注意这个操作不是并行执行的,因为每个棱边的值的更新。

    // 这里回溯操作相对来说不是很长

    for_each(begin(ants), end(ants), [&](Ant& blitz) {

    blitz.Backtrack();

    });

    }

    7)处理取消操作。IAsyncAction,IAsyncActionWithProgress<TProgress>,IAsyncOperation<TResult>,和IAsyncOperationWithProgress<TResult, TProgress>中的每一个接口都会提供一个取消异步操作的“Cancel”方法。可以将任务的取消和WinRT的“Cancel”方法以两种方式关联,首先可以定义传递给create_async的功能函数得到一个Concurrency::cancellation_token对象,当“Cancel”方法被调用时,取消标记(cancellation token)会被撤销。如果没有cancellation_token对象,下层的任务对象会隐式地定义一个,当需要协同响应在功能函数中的取消操作,就定义一个cancellation_token对象。

    取消操作是用户在JavaScript应用中选择取消按钮,或一个不可恢复错误发生时出现的,保持界面在优化任务中响应,是为了让用户能够使用取消,而取消不会立刻发生。C++组件使用Concurrency::cancellation_token_source和Concurrency::cancellation_token标志取消并随机地检测取消操作。从一个执行性能的角度讲,如果应用检测取消的操作过于频繁,或者说检查取消的时间多于应用执行正常工作的时间,对应用的性能会产生影响。C++组件以几种方式检测取消,第一种方式是在每个优化阶段,调用Concurrency::is_task_cancellation_requested函数以确定当前任务是否接受到取消执行的请求之后,出现Continuation任务,如果请求了取消,Continuation调用Concurrency::cancel_current_task 将当前任务置于已撤销状态,下面的代码在从Bing Maps上获取路径之前,获取位置之后的时候检测取消操作。

    //

    // 阶段二: 为每对位置获取路由信息

    //

    // 报告进度

    reporter.report("Retrieving routes (0% complete)...");

    auto tasks = RetrieveRoutes(params, cancellationToken, reporter);

    // 在所有当前任务完成后前往下一阶段

    return when_all(begin(tasks), end(tasks))

    .then([=](task<void>) -> task<IMap<String^, IVector<String^>^>^> {

    //如果有取消则返回

    if (is_task_cancellation_requested()) {

    cancel_current_task();

    return task_from_result<IMap<String^, IVector<String^>^>^>(nullptr);

    }

    // 进度报告

    reporter.report("Retrieving routes (100% complete)...");

    // 记录HTTP消耗时间

    params->HttpTime = GetTickCount64() - params->HttpTime;

    第二种方式是在组件收到其他任务的结果时,通过捕获Concurrency::task_canceled检测取消操作。当每个HTTP请求结束时,会在XML数据处理前检查结果代码,下面的代码展示了TripOptimizerImpl::RetrieveLocations方法怎样执行这样的检查,如果取消操作,Concurrency::task::get方法会抛出task_canceled,这样Continuation会返回并不再处理文档内容。

    // 下载完成后创建Continuation任务填写位置信息

    tasks.push_back(downloadTask.then([=](task<tuple<HRESULT, wstring>> response) {

    try {

    // 如果下载操作失败,抛出COMException异常

    HRESULT hr = get<0>(response.get());

    if (FAILED(hr)) {

    throw ref new COMException(hr);

    }

    }

    // 操作取消了就不再处理文档

    catch (task_canceled&) {

    return;

    }

    try {

    // 创建并加载来自响应中的XML文档

    XmlDocument^ xmlDocument = ref new XmlDocument();

    xmlDocument->LoadXml(ref new String(get<1>(response.get()).c_str()));

    // 填写位置信息

    ProcessLocation(node, xmlDocument, params->UnresolvedLocations);

    }

    catch (Exception^) {

    // 错误发生,取消任何活跃的操作

    m_cancellationTokenSource.cancel();

    // 重新抛出异常

    throw;

    }

    // 进度报告

    wstringstream progress;

    progress << L"Retrieving locations ("

    << static_cast<long>(100.0 * (params->Nodes.size() - params->RequestsPending) / params->Nodes.size())

    << L"% complete)...";

    reporter.report(ref new String(progress.str().c_str()));

    InterlockedDecrement(&params->RequestsPending);

    }, cancellationToken));

    TripOptimizerImpl::RetrieveRoutes方法在每个HTTP请求结束之后,执行了一个相似的检查。接着上面说第三种组件检测取消的方式,是通过调用Concurrency::cancellation_token::is_canceled方法,路线优化算法(即对应的AntSystem::OptimizeRoute函数)以100ms时间为间隔检测取消,代码如下。

    // 执行几次模拟

    auto startTime = GetTickCount64();

    for (unsigned int i = 0; i < iterations; ++i)

    {

    // 随机检测取消

    auto time = GetTickCount64();

    if (time - startTime > 100) {

    if (cancellationToken.is_canceled()) {

    // 返回空集合

    return vector<size_t>();

    }

    startTime = time;

    }

    与以上类似,HttpRequestCallback类使用Concurrency::cancellation_token::register_callback方法注册一个在取消标记被撤销时调用的回调函数,也可以支持操作取消。这个方法很有用,因为IXMLHTTPRequest2接口执行我们控制之外的异步工作,当取消标记被撤销时,回调函数终止HTTP请求并设置任务结束事件,代码如下。

    HttpRequestCallback(IXMLHTTPRequest2 *request, cancellation_token cancellationToken)

    : m_dwRef(1)

    , m_request(request)

    , m_cancellationToken(cancellationToken)

    {

    // 注册当取消标记被撤销时,中止HTTP操作的回调函数

    m_aborted = false;

    m_registrationToken = m_cancellationToken.register_callback([this]() {

    // 设置中止标志

    m_aborted = true;

    if (m_request != nullptr) {

    m_request->Abort();

    }

    m_completionEvent.set(make_tuple<HRESULT, wstring>(S_OK, wstring()));

    });

    }

    cancellation_token::register_callback返回一个Concurrency::cancellation_token_registration对象来识别回调注册。HttpRequest类的析构函数使用这个注册对象,来取消回调函数的注册。最好在不再需要回调函数时取消对其的注册,这样保证在回调函数被调用时所有的对象都是有效的,代码如下。

    ~HttpRequestCallback()

    {

    // Unregister the callback.

    m_cancellationToken.deregister_callback(m_registrationToken);

    }

    遇到不可恢复错误时,任何遗留的任务都会被取消,例如如果无法处理一个XML文档,全部的操作会被取消并抛出异常,代码如下。

    try {

    // Create and load the XML document from the response.

    XmlDocument^ xmlDocument = ref new XmlDocument();

    xmlDocument->LoadXml(ref new String(get<1>(response.get()).c_str()));

    // Fill in location information.

    ProcessLocation(node, xmlDocument, params->UnresolvedLocations);

    }

    catch (Exception^) {

    // An error occurred. Cancel any active operations.

    m_cancellationTokenSource.cancel();

    // Rethrow the exception.

    throw;

    }

  • 相关阅读:
    Delphi-idHttp-JSON用法
    在delphi中, reintroduce作用
    请求转发:MVC设计模式、细节、请求域属性的编程实例、请求重定向和请求转发的区别
    response常见应用、response细节、输出随机图片、定时刷新网页
    HttpServletRequest、request常用方法、request常见应用、请求转发、RequestDispatcher
    Action中取得request,session的四种方式
    List 三种遍历的方法
    java List遍历的方法
    Tag file directory /struts-tags does not start with "/WEB-INF/tags"
    【JavaScript吉光片羽】--- 滑动条
  • 原文地址:https://www.cnblogs.com/finehappy/p/2858187.html
Copyright © 2011-2022 走看看