简介

Accelerate Kit提供了多线程的编程接口,可以让程序更快更高效的运行,可以帮助您更快,更便捷的实现多核,多线程的开发。
Accelerate Kit平台为开发提供了以下多线程编程接口:

本篇Codelab将实现的内容

参照本文档的开发流程,通过文档指定的方式调用接口,实现多线程编程。

您将会学到什么

硬件要求

软件要求

需要的知识点

集成Accelerate Kit能力,需要完成以下准备工作:

  1. 注册成为开发者。
  2. 创建应用。
具体操作,请参见开发指南-配置AppGallery Connect中详细说明来完成。

步骤 1 - 创建Android Stuido Project。

  1. 创建Android Studio工程。
  2. 选择工程模板类型为"Native C++"。
  3. 确定项目名称和选择项目保存位置,点击"Next"。
  4. 点击"Finish",完成新工程的创建。

步骤 2 - 修改gradle文件。
打开/app/build.gradle文件,添加如下信息:

步骤 3 - 复制库和头文件。

  1. SDK包获取,获取路径:SDK下载,下载后解压。
  2. 复制SDK头文件到资源库。
    在Android Studio工程"/app"目录下创建include目录,将SDK中"/include"目录下文件复制到新创建的include目录下:
  3. 复制SDK so到资源库。
    • 在"/app/libs"目录下创建arm64-v8a和armeabi-v7a目录。
    • 将SDK中"/lib64"目录下的"libdispatch.so"、"libdispatch_stat.so"和"libBlocksRuntime.so"复制到Android Studio下的"/libs/arm64-v8a"。
    • 将SDK中"/lib"目录下的"libdispatch.so"、"libdispatch_stat.so"和"libBlocksRuntime.so"复制到Android Studio下的"/libs/armeabi-v7a"。
  4. 修改app/src/main/cpp/CMakeLists.txt。
    需要修改如下部分,下面做简要说明:
    • target_include_directories:添加多线程库头文件所在目录。
    • add_library/set_target_properties:添加引用的多线程库相关库文件。只需要添加libdispatch.so和libBlocksRuntime.so,无需添加libdispatch_stat.so。libdispatch.so中会自动以dlopen的方式添加libdispatch_stat.so。
    • target_compile_options:添加-fblocks编译选项,否则无法使用Block语法。
    • target_link_libraries:添加库文件依赖。
      # For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.4.1) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). native-lib.cpp ) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. target_include_directories( native-lib PRIVATE ${CMAKE_SOURCE_DIR}/../../../include) find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) add_library( dispatch SHARED IMPORTED) set_target_properties( dispatch PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libdispatch.so) add_library( BlocksRuntime SHARED IMPORTED) set_target_properties( BlocksRuntime PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libBlocksRuntime.so) target_compile_options(native-lib PRIVATE -fblocks) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. native-lib dispatch BlocksRuntime # Links the target library to the log library # included in the NDK. ${log-lib} )

—-结束

本小节您将尝试使用Accelerate Kit的主要接口来进行π的计算。在这里我们通过使用格雷戈里-莱布尼茨无穷级数来计算π,公式如下:

我们可以将这个任务分为8个task(task0、...、task7),每个task分别计算这个无穷级数的一部分,最后将这8个和相加从而得到,进而求得,进而求得π。

  1. 设置无穷级数的计算个数n,总任务数threads,并且初始化计算结果pi。由于pi需要在后续的叠加计算中进行更新修改,因此需要加上__block的修饰符。
    _block double pi = 0;
    int n = 1000000;
    int threads = 8;

  2. 调用dispatch_queue_create创建用于计算π的串行队列,其次调用dispatch_apply接口,可以同步地在指定队列重复执行某个任务。建议指定队列设为全局并发队列DISPATCH_APPLY_AUTO,确保其接近当前队列的优先级,此时任务的重复执行就是并发执行的,而本例π的计算具有可重入性,因此可以采用并发执行的方式实现加速计算。
    dispatch_queue_t accumulator = dispatch_queue_create("Compute pi", NULL); dispatch_apply(threads, DISPATCH_APPLY_AUTO, ^(size_t idx){ //π的分步计算实现 });
  3. 实现π的计算算法:将n个级数计算分为threads个任务即8个任务,每一个任务的start和end通过当前任务的idx号来计算得到;idx表示当前任务号,flag表示每一位级数的正负号;sum表示当前任务的计算总和。
    int start = idx * (n / threads); int end = (idx + 1) * (n / threads); double sum = 0; for (int k = start; k < end; k++) { double flag = (k & 1) ? -1.0 : 1.0; sum += flag / (2 * k + 1); }
  4. 调用dispatch_sync用于计算各个任务的sum总和,即8个任务均完成后得到

    dispatch_sync(accumulator, ^{ pi += sum; });
  5. 调用dispatch_release用于释放accumulator的空间。所有dispatch_xxx_create()类型的接口创建的对象都是在堆上申请一定大小的空间。由于没有垃圾回收机制,这类由使用者创建的对象需要在使用者确定不再使用的时候主动释放。
    dispatch_release(accumulator);
  6. 将通过加速计算得到的π以字符串的形式输出到屏幕上,保留到π的小数点后20位。
    extern "C" JNIEXPORT jstring JNICALL Java_com_example_example_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::stringstream ss; ss << std::setprecision(20) << calculate_pi(); return env->NewStringUTF(ss.str().c_str()); }

至此我们完成了π的加速计算。下面给出实例完整代码app/src/main/cpp/native-lib.cpp:

#include <jni.h> #include <string> #include <sstream> #include <iomanip> #include <android/log.h> #include <dispatch/dispatch.h> double calculate_pi(void) { __block double pi = 0; int n = 1000000; int threads = 8; dispatch_queue_t accumulator = dispatch_queue_create("Compute pi", NULL); dispatch_apply(threads, DISPATCH_APPLY_AUTO, ^(size_t idx){ int start = idx * (n / threads); int end = (idx + 1) * (n / threads); double sum = 0; for (int k = start; k < end; k++) { double flag = (k & 1) ? -1.0 : 1.0; sum += flag / (2 * k + 1); } dispatch_sync(accumulator, ^{ pi += sum; }); }); dispatch_release(accumulator); return pi * 4; } extern "C" JNIEXPORT jstring JNICALL Java_com_example_example_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::stringstream ss; ss << std::setprecision(20) << calculate_pi(); return env->NewStringUTF(ss.str().c_str()); }

干得好,您已经成功完成了Accelerate Kit集成并学到了:

您可以阅读下面链接,了解更多相关的信息。

请参见相关文档
本Codelab中所用demo源码下载地址如下:

源码下载

已复制代码