下冰雹 · 2023年06月06日

多线程编程基础知识总结!

什么是多线程编程?

1、线程和进程的区别

进程是指正在运行的程序,它拥有独立的内存空间和系统资源,不同进程之间的数据不共享。

线程是进程内的执行单元,它与同一进程内的其他线程共享进程的内存空间和系统资源。

2、多线程的优势和应用场景

多线程是一种并发编程方式,它的优势包括:

  • 提高程序的响应速度和运行效率(多核CPU下的多线程)
  • 充分利用CPU资源,提高系统的利用率
  • 支持多个任务并行执行,提高程序的可扩展性和可维护性

Linux下的多线程编程

Linux下C语言多线程编程依赖于pthread多线程库。pthread库是Linux的多线程库,是POSIX标准线程API的实现,它提供了一种创建和操纵线程的方法,以及一些同步机制,如互斥锁、条件变量等。

头文件:

`#include <pthread.h>  
`

编译链接需要链接链接库 pthread。

一、线程的基本操作

1、pthread_create

/**
 * @brief 创建一个线程
 *
 * Detailed function description
 *
 * @param[in] thread: 一个指向线程标识符的指针,线程调用后,该值被设置为线程ID;pthread_t为unsigned long int
 * @param[in] attr: 用来设置线程属性
 * @param[in] start_routine: 线程函数体,线程创建成功后,thread 指向的内存单元从该地址开始运行
 * @param[in] arg: 传递给线程函数体的参数
 *
 * @return 线程创建成功,则返回0,失败则返回错误码,并且 thread 内容是未定义的
 */
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg);

例子test.c:创建一个线程,每1s打印一次。

`#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
  
void *thread_fun(void *arg)  
{  
    s_thread_running = 1;  
    while (s_thread_running)  
    {  
        printf("thread run...\n");  
        sleep(1);  
    }  
  
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
  
    printf("Before Thread\n");  
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ret = pthread_join(s_thread_id, NULL); ///< 阻塞等待线程结束  
    if (ret != 0)  
    {  
        printf("pthread_join error!\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("After Thread\n");  
    exit(EXIT_SUCCESS);  
}  
`

编译、运行:

`gcc test.c -o test -lpthread  
`

image.png

2、pthread_join

`/**  
 * @brief 等待某个线程结束  
 *  
 * Detailed function description: 这是一个线程阻塞函数,调用该函数则等到线程结束才继续运行  
 *  
 * @param[in] thread: 某个线程的ID  
 * @param[in] retval: 用于获取线程 start_routine 的返回值  
 *  
 * @return 线程创建成功,则返回0,失败则返回错误码,并且 thread 内容是未定义的  
 */  
int pthread_join(pthread_t thread, void **retval);  
`

例子test.c:创建一个线程,进行一次加法运算就返回。

`#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
  
void *thread_fun(void *arg)  
{  
    static int res = 0;  
    int a = 1, b = 2;  
  
    res = a + b;  
    sleep(1);  
    printf("thread run, a + b = %d, addr = %p\n", res, &res);  
  
    pthread_exit(&res);  
}  
  
int main(void)  
{  
    int ret = 0;  
    int *retval = NULL;  
  
    printf("Before Thread\n");  
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("pthread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ret = pthread_join(s_thread_id, (void **)&retval); ///< 阻塞等待线程结束  
    if (ret != 0)  
    {  
        printf("pthread_join error!\n");  
        exit(EXIT_FAILURE);  
    }  
    if (retval != NULL)  
    {  
        printf("After Thread, retval = %d, addr = %p\n", (int)*retval, retval);  
    }  
  
    exit(EXIT_SUCCESS);  
}  
`

编译、运行:

image.png

3、pthread_exit

`/**  
 * @brief 退出线程  
 *  
 * Detailed function description  
 *  
 * @param[in] retval: 它指向的数据将作为线程退出时的返回值  
 *  
 * @return void  
 */  
void pthread_exit(void *retval);  
`
  1. 线程将指定函数体中的代码执行完后自行结束;
  2. 线程执行过程中,被同一进程中的其它线程(包括主线程)强制终止;
  3. 线程执行过程中,遇到 pthread_exit() 函数结束执行。

例子test.c:创建一个线程,每个1s打印一次,打印超过5次时调用pthread_exit退出。

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
const static char *thread_exit_str = "thread_exit ok!";  
  
void *thread_fun(void *arg)  
{  
    static int cnt = 0;  
      
    s_thread_running = 1;  
    while (s_thread_running)  
    {  
        cnt++;  
        if (cnt > 5)  
        {  
            pthread_exit((void*)thread_exit_str);  
        }  
        printf("thread run...\n");  
        sleep(1);  
    }  
  
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
    void *thread_res = NULL;  
  
    printf("Before Thread\n");  
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ret = pthread_join(s_thread_id, (void**)&thread_res);  
    if (ret != 0)  
    {  
        printf("thread_join error!\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("After Thread, thread_res = %s\n", (char*)thread_res);  
    exit(EXIT_SUCCESS);  
}  

编译、运行:

image.png

使用return退出线程与使用pthread_exit退出线程的区别?

return为通用的函数退出操作,pthread_exit专用与线程,既然pthread库有提供专门的函数,自然用pthread_exit会好些,虽然使用return也可以。

看看return退出线程与使用pthread_exit退出线程的具体区别:退出主线程。使用pthread_exit退出主线程只会终止当前线程,不会影响进程中其它线程的执行;使用return退出主线程,主线程退出执行很快,所有线程都会退出。

例子:使用pthread_exit退出主线程

`#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
const static char *thread_exit_str = "thread_exit ok!";  
  
void *thread_fun(void *arg)  
{  
    sleep(1);  
    printf("thread_fun run...\n");  
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
    void *thread_res = NULL;  
  
    printf("Before Thread\n");  
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
      
    printf("main thread exit\n");  
    pthread_exit(NULL);  
}  
`

编译、运行:

image.png

例子:使用return退出主线程

`#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
const static char *thread_exit_str = "thread_exit ok!";  
  
void *thread_fun(void *arg)  
{  
    sleep(1);  
    printf("thread_fun run...\n");  
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
    void *thread_res = NULL;  
  
    printf("Before Thread\n");  
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
      
    printf("main thread exit\n");  
      
    return 0;  
}  
`

编译、运行:

image.png

4、pthread_self

`/**  
 * @brief 用来获取当前线程ID  
 *  
 * Detailed function description  
 *  
 * @param[in] void  
 *  
 * @return 返回线程id  
 */  
pthread_t pthread_self(void);  
`

例子:

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
const static char *thread_exit_str = "thread_exit ok!";  
  
void *thread_fun(void *arg)  
{  
    static int cnt = 0;  
      
    s_thread_running = 1;  
    while (s_thread_running)  
    {  
        cnt++;  
        if (cnt > 5)  
        {  
            pthread_exit((void*)thread_exit_str);  
        }  
        printf("thread run(tid = %ld)...\n", pthread_self());  
        sleep(1);  
    }  
  
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
    void *thread_res = NULL;  
  
    pid_t pid = getpid();  
    printf("pid = %d\n", pid);  
  
    printf("Before Thread\n");  
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ret = pthread_join(s_thread_id, (void**)&thread_res);  
    if (ret != 0)  
    {  
        printf("thread_join error!\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("After Thread, thread_res = %s\n", (char*)thread_res);  
    exit(EXIT_SUCCESS);  
}  

编译、运行:

image.png

5、pthraad_detach

/**  
 * @brief 分离线程  
 *  
 * Detailed function description: 分离线程,线程结束是系统自动回收线程的资源  
 *  
 * @param[in] thread: 某个线程的ID  
 *  
 * @return 成功时返回0,失败返回其他值  
 */  
int pthread_detach(pthread_t thread);  

pthread_create创建的线程有两种状态:joinable(可结合的)和unjoinable(不可结合的/分离的)。默认是joinable 状态。

一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的,所以以默认的属性创建线程时,创建的线程时可结合的,我们需要对线程退出时调用pthread_join对线程资源进行回收。只有当pthread_join函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。

一个不可结合的线程,线程结束后会自动释放占用资源。

因为pthread_join是一个阻塞的操作,而大多数时候主线程并不希望因为调用pthread_join而阻塞,并且大多数情况下不会使用线程函数体的返回值,所以这时候可以把线程创建为不可结合的/分离的。

把线程创建为不可结合的/分离的有两种方式:

  • 在创建线程之后,使用pthraad_detach分离线程。
  • 在创建线程之前,使用pthread_attr_setdetachstate设置线程以不可结合的/分离的状态创建。

例子:在创建线程之后,使用pthraad_detach分离线程。

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
  
void *thread_fun(void *arg)  
{  
    s_thread_running = 1;  
    while (s_thread_running)  
    {  
        printf("child thread run...\n");  
        sleep(1);  
    }  
  
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
  
    printf("Before Thread\n");  
    ret = pthread_create(&s_thread_id, NULL, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ret = pthread_detach(s_thread_id);  
    if (ret != 0)  
    {  
        printf("pthread_detach error!\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("After Thread\n");  
  
    while (1)  
    {  
        printf("main thread run...\n");  
        sleep(1);  
    }  
      
    exit(EXIT_SUCCESS);  
}  

编译、运行:

image.png

pthread_join与pthraad_detach的区别:

  • pthread_detach()即主线程与子线程分离,两者相互不干涉,子线程结束同时子线程的资源自动回收。
  • pthread_join()即是子线程合入主线程,主线程会一直阻塞,直到子线程执行结束,然后回收子线程资源,并继续执行。

6、pthread_attr_init

`/**  
 * @brief 初始化一个线程对象的属性  
 *  
 * Detailed function description  
 *  
 * @param[in] attr: 指向一个线程属性的指针  
 *  
 * @return 成功时返回0,失败返回其他值  
 */  
int pthread_attr_init(pthread_attr_t *attr);  
`

如果不设置线程属性,线程则以默认属性进行创建,默认的属性值如:

image.png

例子:在创建线程之前,使用pthread_attr_setdetachstate设置线程以不可结合的/分离的状态创建。

`#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread_id;  
static unsigned char s_thread_running = 0;  
  
void *thread_fun(void *arg)  
{  
    s_thread_running = 1;  
    while (s_thread_running)  
    {  
        printf("thread run...\n");  
        sleep(1);  
    }  
  
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
  
    printf("Before Thread\n");  
    pthread_attr_t attr;  
    ret = pthread_attr_init(&attr);  
    if (ret != 0)  
    {  
        printf("pthread_attr_init error!\n");  
        exit(EXIT_FAILURE);  
    }  
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);  ///< 线程以分离的状态创建  
    ret = pthread_create(&s_thread_id, &attr, thread_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("After Thread\n");  
    pthread_attr_destroy(&attr);  ///< 销毁线程属性结构  
  
    while (1)  
    {  
        sleep(1);  
    }  
      
    exit(EXIT_SUCCESS);  
}  
`

二、互斥锁(mutex)的使用

互斥锁用于保护一些公共资源。一些公共资源有可能会被多个线程共同使用,如果不做资源保护,可能会产生                                                      意想不到的bug。

一个线程,如果需要访问公共资源,需要获得互斥锁并对其加锁,资源在在锁定过程中,如果其它线程对其进行访问,也需要获得互斥锁,如果获取不到,线程只能进行阻塞,直到获得该锁的线程解锁。

互斥锁API:

`#include<pthread.h>    
///< 创建互斥对象,用指定的初始化属性初始化互斥对象    
int pthread_mutex_init(pthread_mutex_t *mutex,    
                                       const pthread_mutex_attr_t *mutexattr);    
  
///< 加锁    
int pthread_mutex_lock(pthread_mutex_t *mutex);    
  
///< 解锁    
int pthread_mutex_unlock(pthread_mutex_t *mutex);    
  
///< 加锁,但是如果对象已经上锁则返回EBUSY错误代码而不阻塞    
int pthread_mmutex_trylock(pthread_mutex_t *mutex);    
  
///< 析构并释放mutex相关资源    
int pthread_mutex_destroy(pthread_mutex_t *mutex);    
`

互斥锁有两种创建方式:

  • 静态创建:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  • 动态创建:
`pthread_mutex_t mutex;  
pthread_mutex_init(&mutex, NULL);  
`

pthread互斥锁属性包括:

  • PTHREAD_MUTEX_TIMED_NP:这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将会形成一个等待队列,并在解锁后按优先级获得锁。这种策略可以确保资源分配的公平性。
  • PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁。允许同一个线程对同一个锁成功获得多次,并通过unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
  • PTHREAD_MUTEX_ERRORCHECK_NP:检错锁。如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同,这样就保证了当不允许多次加锁时不会出现最简单情况下的死锁。
  • PTHREAD\_MUTEX_ADAPTIVE_NP:适应锁,动作最简单的锁类型,仅等待一小段时间,如果不能获得锁就放弃等待

互斥锁使用形式:

`pthread_mutex_t mutex;  
pthread_mutex_init(&mutex,NULL);  ///< 初始化互斥锁  
pthread_mutex_lock(&mutex);       ///< 加锁  
///< 操作公共资源  
pthread_mutex_unlock(&mutex);     ///< 解锁  
pthread_mutex_destroy(&mutex);    ///< 销毁互斥锁  
`

例子:

`#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread1_id;  
static pthread_t s_thread2_id;  
static unsigned char s_thread1_running = 0;  
static unsigned char s_thread2_running = 0;  
  
static pthread_mutex_t s_mutex;  
static int s_cnt = 0;  
  
void *thread1_fun(void *arg)  
{  
     printf("[%s]pthread_mutex_lock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);  
    pthread_mutex_lock(&s_mutex);    ///< 加锁  
    for (size_t i = 0; i < 100; i++)  
    {  
        s_cnt++;  
    }  
    printf("[%s]pthread_mutex_unlock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);  
    pthread_mutex_unlock(&s_mutex);  ///< 解锁  
      
    pthread_exit(NULL);  
}  
  
void *thread2_fun(void *arg)  
{  
    printf("[%s]pthread_mutex_lock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);  
    pthread_mutex_lock(&s_mutex);    ///< 加锁  
    for (size_t i = 0; i < 100; i++)  
    {  
        s_cnt++;  
    }  
    printf("[%s]pthread_mutex_unlock ------ s_cnt = %d\n", __FUNCTION__, s_cnt);  
    pthread_mutex_unlock(&s_mutex);  ///< 解锁  
      
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
  
    ///< 创建互斥量  
    ret = pthread_mutex_init(&s_mutex, NULL);  
    if (ret != 0)  
    {  
        printf("pthread_mutex_init error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ///< 创建线程1  
    ret = pthread_create(&s_thread1_id, NULL, thread1_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread1_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
    ret = pthread_join(s_thread1_id, NULL); ///< 阻塞等待线程结束  
    if (ret != 0)  
    {  
        printf("pthread1_join error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ///< 创建线程2  
    ret = pthread_create(&s_thread2_id, NULL, thread2_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread2_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
    ret = pthread_join(s_thread2_id, NULL); ///< 阻塞等待线程结束  
    if (ret != 0)  
    {  
        printf("pthread2_join error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    printf("main thread, s_cnt = %d\n", s_cnt);  
  
    ret = pthread_mutex_destroy(&s_mutex);  
    {  
        printf("pthread_mutex_destroy error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    return 0;  
}  
`

编译、运行:

image.png

三、条件变量的使用

条件变量是在线程中以睡眠的方式等待某一条件的发生,是利用线程间共享的全局变量进行同步的一种机制。

条件变量是线程可用的一种同步机制,条件变量给多个线程提供了一个会合的场所,条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。

条件变量API:

#include <pthread.h>  
///< 条件变量初始化  
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);  
  
///< 销毁条件变量  
int pthread_cond_destroy(pthread_cond_t *cond);  
  
///< 等待条件变量  
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);  
  
///< 带有超时功能的 等待条件变量  
int pthread_cond_timedwait(pthread_cond_t *restrict cond,   
      pthread_mutex_t *restrict mutex,  
      const struct timespec *restrict tsptr);  
  
///< 通知条件变量,唤醒至少1个等待该条件的线程  
int pthread_cond_signal(pthread_cond_t *cond);  
  
///< 通知条件变量,广播唤醒等待该条件的所有线程  
int pthread_cond_broadcast(pthread_cond_t *cond);  

假如有两个线程,线程1依赖于某个变量才能执行相应的操作,而这个变量正好是由线程2来改变的。这种情况下有两种方案编写程序:

方案一:线程1轮询的方式检测这个变量是否变化,变化则执行相应的操作。

方案二:使用条件变量的方式。线程1等待线程2满足条件时进行唤醒。

其中,方案一比较浪费CPU资源。

条件变量的例子:创建两个线程,线程1对全局计数变量cnt从0开始进行自增操作。线程2打印5的倍数,线程1打印其它数。

`#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
static pthread_t s_thread1_id;  
static pthread_t s_thread2_id;  
static unsigned char s_thread1_running = 0;  
static unsigned char s_thread2_running = 0;  
  
static pthread_mutex_t s_mutex;  
static pthread_cond_t s_cond;  
static int s_cnt = 0;  
  
void *thread1_fun(void *arg)  
{  
    s_thread1_running = 1;  
    while (s_thread1_running)    
    {  
        pthread_mutex_lock(&s_mutex);    ///< 加锁  
        s_cnt++;  
        pthread_mutex_unlock(&s_mutex);  ///< 解锁  
  
        if (s_cnt % 5 == 0)  
        {  
            pthread_cond_signal(&s_cond);  ///< 唤醒其它等待该条件的线程  
        }  
        else  
        {  
            printf("[%s]s_cnt = %d\n", __FUNCTION__, s_cnt);  
        }  
  
        usleep(100 * 1000);  
    }  
      
    pthread_exit(NULL);  
}  
  
void *thread2_fun(void *arg)  
{  
    s_thread2_running = 1;  
    while (s_thread2_running)  
    {  
        pthread_mutex_lock(&s_mutex);    ///< 加锁  
        while (s_cnt % 5 != 0)  
        {  
            pthread_cond_wait(&s_cond, &s_mutex);   ///< 等待条件变量  
        }  
          
        printf("[%s]s_cnt = %d\n", __FUNCTION__, s_cnt);  
        pthread_mutex_unlock(&s_mutex);  ///< 解锁  
          
        usleep(200 * 1000);  
    }  
      
    pthread_exit(NULL);  
}  
  
int main(void)  
{  
    int ret = 0;  
  
    ///< 创建互斥量  
    ret = pthread_mutex_init(&s_mutex, NULL);  
    if (ret != 0)  
    {  
        printf("pthread_mutex_init error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ///< 创建条件变量  
    ret = pthread_cond_init(&s_cond, NULL);  
    if (ret != 0)  
    {  
        printf("pthread_cond_init error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ///< 创建线程1  
    ret = pthread_create(&s_thread1_id, NULL, thread1_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread1_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
    ret = pthread_detach(s_thread1_id);  
    if (ret != 0)  
    {  
        printf("s_thread1_id error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    ///< 创建线程2  
    ret = pthread_create(&s_thread2_id, NULL, thread2_fun, NULL);  
    if (ret != 0)  
    {  
        printf("thread2_create error!\n");  
        exit(EXIT_FAILURE);  
    }  
    ret = pthread_detach(s_thread2_id);  
    if (ret != 0)  
    {  
        printf("s_thread2_id error!\n");  
        exit(EXIT_FAILURE);  
    }  
  
    while (1)  
    {  
        sleep(1);  
    }  
  
    return 0;  
}  
`

编译、运行:

image.png

以上就是本次的分享,如果觉得文章有用,欢迎收藏、转发!

相关资料:

作者:LinuxZn
文章来源:嵌入式大杂烩

推荐阅读

更多嵌入式AI干货请关注嵌入式AI专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
16328
内容数
1218
嵌入式端AI,包括AI算法在推理框架Tengine,MNN,NCNN,PaddlePaddle及相关芯片上的实现。欢迎加入微信交流群,微信号:aijishu20(备注:嵌入式)
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息