C语言linux线程库pthread的简单使用教程

⌚Time: 2023-01-25 17:17:49

👨‍💻Author: Jack Ge

POSIX线程(pthread)库

POSIX线程库是用于C/C++的基于标准的线程API。它允许产生一个新的并发流程。它在多处理器或多核系统上最为有效,在这些系统中,可以将流程安排在另一个处理器上运行,从而通过并行或分布式处理提高速度。线程比“forking”或生成新进程所需的开销更少,因为系统不会为进程初始化新的系统虚拟内存空间和环境。虽然在多处理器系统上最有效,但在单处理器系统上也可以找到增益。

使用线程在图形界面程序尤为有效,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。

pthread库需要头文件

pthread.h

编译链接参数-lpthread

创建线程

数据类型

pthread_t 线程ID

pthread_attr_t 线程属性

创建线程函数


int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

参数说明

thread:返回线程ID, (在bits/pthreadtypes.h文件中定义为unsigned long int类型)

attr:线程属性,如果要使用默认属性直接传递 NULL

start_routine:线程函数,它是一个函数指针类型,返回类型为 void *,参数为一个 void * 类型变量,创建好这样类型的一个函数,将函数名传递进去即可。

arg:线程函数参数的指针,如果有多个参数,可以传递一个指向参数结构体的指针

返回值说明

成功情况下返回 0,失败情况下返回错误码

获取线程ID


pthread_t pthread_self(void);

该函数返回调用它的线程的线程ID

通过线程ID比较线程是否相等


int pthread_equal(pthread_t t1, pthread_t t2);

如果两个线程相等,返回非0值,如果不相等,返回0

分离线程


int pthread_detach(pthread_t thread);

将线程 ID 为 thread 的线程分离出去,所谓分离出去就是指主线程不需要再通过 pthread_join 等方式等待该线程的结束并回收其线程控制块(TCB)的资源,被分离的线程结束后由操作系统负责其资源的回收。

成功情况下返回 0,失败情况下返回错误码。

额外说明

一般来说,主线程是要负责创建出来的子线程的资源回收工作的。如果主线程先于子线程退出并且子线程没有设置为分离状态,那么子线程结束后其资源是无法得到回收的,会造成资源浪费和系统臃肿;如果主线程先于子线程退出但是子线程时分离状态,那么子线程退出的时候操作系统会自动回收其资源。


“分离线程”并不是“分离”了之后跟主线程没有一点关系,主线程退出了,“分离线程”还是一样退出。只是“分离线程”的资源是由系统回收的。

终止线程

终止线程的三种方式

a. 线程从启动例程返回,返回值就是线程的退出码

b. 线程可以被同一进程中的其他线程取消

取消线程


int pthread_cancel(pthread_t thread);

向线程发送取消请求,成功返回0,失败返回错误码

c. 线程自身调用pthread_ exit()函数

终止线程


void pthread_exit(void *retval);

参数

retval 返回值

pthread_exit()不返回任何值,如果线程未分离,则可以使用pthread_join()从另一个线程检查线程id和返回值。

注意:指针*retval指向的内容不能为函数中局部变量,因为一旦线程函数终止,它们将不再存在。

注意:如果在主线程中调用了 pthread_exit(NULL),则主线程退出,而不是退出进程,因此如果子线程存在,会继续执行。

等待线程结束


int pthread_join(pthread_t th, void **thread_return);

参数:

th 阻塞当前线程,直到th指定的线程终止。线程终止可以通过调用pthread_exit()函数或者被取消

thread_return 如果线程返回不为NULL,返回值将被储存在thread_return

可以在主线程中使用pthread_join()等待子线程执行完成后再结束程序

测试线程的使用


#include <unistd.h>

#include <stdio.h>

#include <pthread.h>

void* func_a(void *pointer){

    int i = 0;

    for(i = 0; i < 3; i++){

        sleep(1);

        printf("thread %d running\n",*(int*)&pointer);



    }

}

int main(int argc, char* argv[]){

    pthread_t threadID[5];

    int i = 0;

    //创建线程

    for(i = 0; i < 5; i++){

        pthread_create(&threadID[i],NULL,func_a,(void *)i);

    }

    //等待所有线程结束后再退出主线程

    for(i = 0; i < 5; i++){

        pthread_join(threadID[i],(void**)0);

    }

    return 0;

}


线程同步

pthread线程库提供三种同步机制:

互斥锁:阻止其他线程访问变量。这强制线程对变量或变量集进行独占访问。

pthread_join() 使线程等待其他线程结束。

条件变量-数据类型 pthread_cond_t

使用线程锁进行数据同步的办法:

线程锁函数


pthread_mutex_t lock; /* 互斥锁定义 */

pthread_mutex_init(&lock, NULL); /* 动态初始化,  成功返回0,失败返回非0 */

pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化 */

pthread_mutex_lock(&lock); /* 阻塞的锁定互斥锁 */

pthread_mutex_trylock(&thread_mutex);/* 非阻塞的锁定互斥锁,成功获得互斥锁返回0,如果未能获得互斥锁,立即返回一个错误码 */

pthread_mutex_unlock(&lock); /* 解锁互斥锁 */

pthread_mutex_destroy(&lock) /* 销毁互斥锁 */


测试线程锁的代码


#include <unistd.h>

#include <stdio.h>

#include <pthread.h>

//定义互斥锁

pthread_mutex_t g_mutex;

int g_ia = 0;

void *func_c(void *pointer){

    while(1){

        //加锁

        //pthread_mutex_lock(&g_mutex);

        g_ia++;

        //解锁

        //pthread_mutex_unlock(&g_mutex);

        sleep(1);

        

    }

    pthread_exit((void**)0);    

}

int main(int argc, char* argv[]){

    pthread_t threadID;

    pthread_create(&threadID,NULL,func_c,NULL);

    //初始化锁

    pthread_mutex_init(&g_mutex,NULL);

    int i = 0;

    for(i = 0; i<5; i++){

        //加锁

        //pthread_mutex_lock(&g_mutex);

        fprintf(stderr,"%d ",g_ia);

        sleep(1);

        fprintf(stderr,"%d ",g_ia);

        sleep(1);

        fprintf(stderr,"%d \n",g_ia);

        //解锁

        //pthread_mutex_unlock(&g_mutex);

        sleep(1);



    }   

    pthread_cancel(threadID);   

    return 0;

}


不加锁,在一个循环内读取频繁的被线程修改

将加锁的语句注释取消,对数据加锁保护,在每个while循环内数据没有被线程修改