Natice层线程创建的方法

本文介绍了两种在native层创建线程的方法:直接使用pthread_create函数及通过继承Thread类实现。详细解析了线程启动流程、函数调用过程以及线程名称设置等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有两个方法在native层创建thread:

1.直接使用pthread_create函数

bionic/libc/bionic/pthread_create.cpp
注册的函数是怎样被调用到的?
int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
                   void* (*start_routine)(void*), void* arg) {


  pthread_internal_t* thread = NULL;
  void* child_stack = NULL;
  int result = __allocate_thread(&thread_attr, &thread, &child_stack);




  thread->start_routine = start_routine;
  thread->start_routine_arg = arg;


  thread->set_cached_pid(getpid());




  int rc = clone(__pthread_start, child_stack, flags, thread, &(thread->tid), tls, &(thread->tid));


  int init_errno = __init_thread(thread);


  // Publish the pthread_t and unlock the mutex to let the new thread start running.
  *thread_out = __pthread_internal_add(thread);
  pthread_mutex_unlock(&thread->startup_handshake_mutex);


  return 0;
}


static int __pthread_start(void* arg) {
  pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(arg);


  void* result = thread->start_routine(thread->start_routine_arg);
  pthread_exit(result);


  return 0;
}


线程的名字是怎样被更改的?默认是?
prctl(PR_SET_NAME, "xxxx");

2. 使用继承基类Thread的方法

方法1 创建thread还是比较简单的,直观的。但是下面这种方式,就不太直观
要找到基类Thread的实现,在哪里? 看头文件就行,通过头文件,和文件名等,最终找到
Thead基类的实现system/core/libutils/Threads.cpp
    class EventThread : public Thread {
    public:
        EventThread(const char* sourceName, int sourceType);


        virtual bool threadLoop();
        const char* getName(){ return mSourceName.string(); }
    private:
        int mSourceType;
        String8 mSourceName;
    };
从中可以看到,主干函数threadLoop是怎样被调到的。
monitor_thread->run(monitor_thread->getName(), PRIORITY_NORMAL);


status_t Thread::run(const char* name, int32_t priority, size_t stack)
{


    mThread = thread_id_t(-1);


    // hold a strong reference on ourself
    mHoldSelf = this;


    mRunning = true;


    if (mCanCallJava) {
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }




    return NO_ERROR;
}


int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
                               void *userData,
                               const char* threadName __android_unused,
                               int32_t threadPriority,
                               size_t threadStackSize,
                               android_thread_id_t *threadId)
{


    pthread_t thread;
    int result = pthread_create(&thread, &attr,
                    (android_pthread_entry)entryFunction, userData);


}


pthread_create创建线程的入口函数是已知的:_threadLoop
int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user);
    bool first = true;


    result = self->threadLoop();


    return 0;
}


self->threadLoop();这个就是子类生成的主函数

<think>嗯,用户这次询问的是AndroidNative用C++创建线程的具体方法,特别提到了pthread的示例。从历史记录看,之前讨论过C++11的std::thread用法,但用户现在明确需要原生pthread的方案,可能正在处理NDK开发或底移植相关的工作。用户引用的材料很有针对性:引用[1]展示了std::thread的基础用法,引用[2][3]则提到Android设置线程名的坑和底机制。这说明用户应该遇到过线程命名失效的问题,可能需要特别注意pthread_attr_setname_np这个限制。具体需求可以拆解为:1基本线程创建流程(用户明确要求pthread示例)2线程属性设置(特别是名字设置这个易错点)3资源回收机制(避免线程泄漏)考虑到native开发的实际场景,需要强调两个重点:-pthread_create的返回值检查(Android上容易因权限问题失败)-分离状态设置(DETACHEDvsJOINABLE)对资源管理的影响用户提到“示例”,那就应该提供完整可编译的代码片段。但要注意代码必须包含错误处理,因为引用[2]特别强调了设置线程名可能失效的情况,这正是Android开发的常见痛点。突然想到还有个隐含需求:用户可能需要对比pthread和std::thread的优劣。虽然问题没明说,但既然引用[1]提到std::thread,或许后续会有相关疑问。这次回复里先埋个伏笔比较合适。安全方面要注意:在线程函数里访问JNIEnv是危险操作,但用户没问就不展开,避免混淆核心问题。不过示例代码里的全局字符串传参需要加volatile修饰吗?看来用结构体指针传参更稳妥。</think> 好的,这是在Android Native(C++)使用POSIX Threads (`pthread`) 创建线程的详细示例和说明。结合您提供的引用材料,我将重点突出Android Native开发中的关键点和注意事项。 ### 核心函数:`pthread_create` POSIX标准的核心线程创建函数是 `pthread_create`,其原型如下: ```cpp #include <pthread.h> int pthread_create(pthread_t* thread, const pthread_attr_t* attr, // 线程属性,可为NULL void* (*start_routine)(void*), // 线程函数指针 void* arg); // 传递给线程函数的参数 ``` ### 完整的创建、运行和回收示例 ```cpp #include <pthread.h> #include <unistd.h> // for sleep(), usleep() #include <android/log.h> // Android日志输出 #define TAG "NativeThreading" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) // 线程函数 - 必须符合 void* (*)(void*) 签名 void* thread_function(void* arg) { const char* thread_name = static_cast<const char*>(arg); // 接收传入的参数 LOGD("线程 '%s' 开始执行 - TID: %d", thread_name, gettid()); // 模拟工作 (计算、I/O等) for (int i = 0; i < 5; ++i) { LOGD("'%s' - 任务进度: %d/5", thread_name, i+1); usleep(500000); // 休眠500毫秒 (微秒单位) } LOGD("线程 '%s' 执行完毕", thread_name); return nullptr; // 线程返回值,这里无具体意义 } int main() { pthread_t thread_id; const char* thread_name = "WorkerThread#1"; LOGD("主线程 (PID: %d, TID: %d) 即将创建线程", getpid(), gettid()); // 创建线程 (属性用默认值NULL, 传入线程名) int result = pthread_create(&thread_id, nullptr, thread_function, (void*)thread_name); if (result != 0) { LOGD("线程创建失败! 错误码: %d", result); return -1; } LOGD("成功创建线程,ID: %lu", (unsigned long)thread_id); // 注意打印方式 // 主线程继续自己的工作... sleep(1); LOGD("主线程继续执行其他任务..."); // 等待子线程结束(回收资源) - 必需步骤! void* thread_return; result = pthread_join(thread_id, &thread_return); if (result != 0) { LOGD("等待线程结束失败! 错误码: %d", result); } else { LOGD("已回收线程 '%s' 资源", thread_name); } LOGD("程序正常退出"); return 0; } ``` ### 关键点解释 (结合引用材料) 1. **线程函数 (`start_routine`):** * 必须定义为 `void* function_name(void* arg)` 的格式。 * `void* arg` 用于接收 `pthread_create` 的第四个参数(`arg`)传递进来的数据。可以传递任何数据的指针(如结构体、对象指针、基本类型的指针等)。示例中传递了一个 `const char*`。 * 返回值也是 `void*`。如果不需要返回值,返回 `nullptr` 即可。需要时,可以返回复杂结构的指针。调用 `pthread_join` 可以捕获这个返回值。 * 避免直接使用 JNIEnv 指针(它是线程相关的),如需使用 JNI,必须通过 `AttachCurrentThread`/`GetEnv` 获取当前线程的 `JNIEnv`。 2. **线程标识符 (`pthread_t thread_id`):** * 用于唯一标识一个线程。 * `pthread_create` 成功时,将在此变量中存储新线程的 ID。 * 用于后续的线程操作(如 `pthread_join`, `pthread_cancel`, 设置线程属性等)。注意其打印方式可能需要类型转换 `(unsigned long)`。 3. **线程属性 (`pthread_attr_t attr`):** * 用于设置新线程的属性(栈大小、调度策略、分离状态等)。如果使用默认值(NULL),大部分属性符合预期。 * **设置线程名(避免 `PR_SET_NAME` 失效):** 如引用[2]所述,在 Android 上通过 `prctl(PR_SET_NAME, ...)` 设置 native 线程名有时会失效。更可靠的做法是在线程函数*内部*调用 `pthread_setname_np` (Android NDK API Level 9+ 支持): ```cpp void* thread_function(void* arg) { pthread_setname_np(pthread_self(), "MyWorker"); // ... 线程工作 ... } ``` 4. **连接线程 (`pthread_join`):** * **关键步骤!** 在子线程创建后,主线程(或父线程)通常需要调用 `pthread_join` 来等待子线程终止。 * `pthread_join` **阻塞**调用它的线程,直到指定的线程终止。 * 回收线程的资源(如退出状态值、堆栈内存等),**防止资源泄露**。 * 第二个参数 `thread_return` 是一个指向 `void*` 的指针,用于接收线程函数的返回值。如果不需要返回值,可以传入 `NULL`。 5. **分离线程 (`pthread_detach`):** * 如果**不需要**等待线程结束或获取其返回值(例如后台任务),可以将线程设置为*分离(detached)*状态。 * 方式: * 创建时设置属性:`pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);` 然后传入 `attr`。 * 创建后分离:`pthread_detach(thread_id);` * 分离线程终止时,其资源会自动被系统回收。**不能对分离线程调用 `pthread_join`**。 * 避免忘记 `pthread_join` 导致资源泄露的好方法,适用于纯粹的后台任务。 6. **Android 注意事项:** * **使用 `__android_log_print`:** 在 Native 代码中使用 `android/log.h` 提供的日志宏(如示例中的 `LOGD`)进行调试输出,比 `std::cout` 更方便在 `logcat` 中查看。 * **线程名设置可靠性:** 如引用[2]强调,在线程函数**内部**调用 `pthread_setname_np` 比在外部通过 `prctl` 设置更可靠,特别是在某些 Android 版本上。 * **JNI 交互:** 在线程中调用 Java 方法时,必须正确获取属于当前 `pthread` 的 `JNIEnv`。通常做法是: 1. 在创建线程前获取全局的 `JavaVM` 指针(通过 `JNI_OnLoad` 或作为参数传递)。 2. 在线程函数开始部分,使用 `AttachCurrentThread` 或 `GetEnv` 获取当前线程的 `JNIEnv`。 3. 在线程函数结束前(或不再需要调用 Java 时),使用 `DetachCurrentThread`。 * **NDK 版本:** 确保使用的 `pthread` 函数在项目配置的 NDK 版本和目标 API Level 上都支持。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值