linux内核学习-线程-线程控制

mac2026-09-23  12

线程:可以理解为轻量级的CPU执行单元。其必须依托在进程内部。

同一个进程里面的多个线程是共享进程的资源的。但是线程也有自己的私有数据,包括:

线程号(thread ID)寄存器堆栈信号掩码优先级线程私有存储空间

线程的头文件为pthread.h 链接库为libpthread.a

一、创建线程

线程常用函数

函数说明pthread_t pthread_self(void)获取本线程的线程IDint pthread_equal(pthread_t thread1,pthread_t thread2)判断两个线程ID是否指向同一个线程int pthread_once(pthread_once_t *once_control,void(*int_routine)(void))用来保证init_routine线程函数在进程中仅执行一次

测试程序

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int* thread(void *arg) { pthread_t newthid; newthid = pthread_self(); printf("this is a new thread ,thread ID =%u\n",newthid); return NULL; } int main(void) { pthread_t thid; printf("main thread ,ID is %u\n",pthread_self());//打印主线程的ID if(pthread_create(&thid,NULL, (void *)thread,NULL)!=0) { printf("thread creation failed \n"); exit(1); } sleep(1); exit(0); }

gcc在编译的时候,不指定库,会报错 原因 由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数: gcc -o pthread -lpthread pthread.c

一个有趣的现象 我在程序中死循环创建线程,且线程不退出,发现,CPU的使用率确实很高,但是整个系统并没有卡死的现象。

二、线程属性

参数pthread_attr_t结构体的具体含义 datachstate:表示创建的线程是否与进程中的其他线程脱离同步。 schedpolicy:新线程调度策略 schedparam:调度涉及到的一个结构体(sched_param) inheritsched:调度是否用继承的。 scop:线程间的CPU竞争范围 guardsize:警戒堆栈大小 stackaddr_set:堆栈地址集 stacksize:堆栈的大小

三、线程终止

使用pthread_exit() 有两种情况会导致线程退出:1.main函数return或exit了。2.主线程调用了pthread_exit。 线程终止,需要关注两个大问题: 1.临界资源(就是资源在一个时间内只有一个线程能用,想用的线程只能等待。)。在终止前,需要做好释放工作。具体如下: linux系统提供了一对函数:pthread_cleanup_push()和pthread_cleanup_pop()用于自动释放资源。在使用时,这两个函数必须成对使用,且在同一段代码内部。(原因是这两个函数的本质其实是一段宏代码)。 2.线程同步问题。线程中也有类似进程的wait函数,其作用就是等待某一个线程结束了自己才结束。对应的函数是pthread_join()。需要注意

被等待的线程不能处于DETACHED状态(既调用pthread_detach函数)。一个线程不能被多个线程等待。否则出了第一个会正常收到返回,其他都会返回错误代码ESRCH。

示例代码:

#include <stdio.h> #include <pthread.h> void assisthread(void *arg) { printf("I am helping to do some jobs\n"); sleep(3); pthread_exit(0); } int main(void) { pthread_t assistthid; int status; pthread_create(&assistthid,NULL ,(void *)assisthread,NULL); pthread_join(assistthid,(void*)&status); printf("assistthread's exit is cause %d\n",status); return 0; }

运行结果:

gcc -o jointhread jointhread.c -lpthread #编译 svauto@ubuntua:~/C/thread$ ./jointhread #运行 I am helping to do some jobs assistthread's exit is cause 0

程序可以看出主线程在执行pthread_join后处于等待状态,知道子线程退出后,才退出主线程。

四、私有数据

在多线程中,进程的数据空间是共享的,有时候线程需要有自己的私有全局数据,应该怎么做呢? 线程私有数据使用:“一键多值”技术。创建“键”的时候,Linux从TSD池中分配一项,当一个键被创建后,所有的线程都可以访问,但是不同线程从这个键中取到的值是不一样的。这就是一键多值的含义。 涉及到的函数有四个

函数作用pthread_key_create创建一个键pthread_setspecific为一个键设置线程私有数据pthread_getspecific从一个键获取私有数据pthread_key_delete删除一个键

实例代码:

#include <stdio.h> #include <string.h> #include <pthread.h> pthread_key_t key; void * thread2(void *arg) { int tsd=5; printf("thread %x is running \n",pthread_self()); pthread_setspecific(key,(void *)tsd); printf("thread %x returns %d \n",pthread_self(),pthread_getspecific(key)); } void *thread1(void *arg) { int tsd = 0; pthread_t thid2; printf("thread %x is running \n",pthread_self()); pthread_setspecific(key,(void *)tsd); pthread_create(&thid2,NULL,thread2,NULL); sleep(2); printf("thread %x returns %d\n",pthread_self(), pthread_getspecific(key)); } int main(void) { pthread_t thid1; printf("main thread begins running\n"); pthread_key_create(&key,NULL); pthread_create(&thid1,NULL,thread1,NULL); sleep(3); pthread_key_delete(key); printf("main thread exit\n"); return 0; }

运行结果: 可以看出两个线程的私有数据,是不会互相影响的。

五、线程同步

linux有3种方式处理线程的同步:

互斥锁条件变量异步信号

1.互斥锁 在同一时刻,通常只允许一个线程执行一个关键部分的代码。涉及到的函数如下

函数功能pthread_mutex_init初始化一个互斥锁pthread_mutex_destroy注销一个互斥锁pthread_mutex_lock加锁,如果不成功则阻塞等待pthread_mutex_unlock解锁pthread_mutex_trylock测试加锁,如果不成功则立即返回,错误码为EBUSY

互斥锁初始化 初始化方法有两种 实例代码 下面代码用互斥锁的方式,保护全局变量在各线程之间同步(注意:此时不能在代码中直接操作全局变量,而应该调用下面的read ,write函数)

#include <stdio.h> #include <string.h> #include <pthread.h> pthread_mutex_t number_mutex; int globalnumber; void write_globalnumber() { pthread_mutex_lock(&number_mutex); globalnumber++; printf("now globalnumber=%d\n",globalnumber); pthread_mutex_unlock(&number_mutex); } int read_globalnumber() { int temp; pthread_mutex_lock(&number_mutex); temp= globalnumber; pthread_mutex_unlock(&number_mutex); return (temp); } void * thread2(void *arg) { int tsd=5,i=20; printf("thread2 %x is running \n",pthread_self()); while(i>0) { printf("read =%d\n",read_globalnumber()); i--; usleep(1); } } void *thread1(void *arg) { int tsd = 0,i=10; pthread_t thid2; printf("thread1 %x is running \n",pthread_self()); while(i>0) { write_globalnumber(); i--; usleep(1); } } int main(void) { pthread_t thid1,thid2; int statue; printf("main thread begins running\n"); pthread_create(&thid1,NULL,thread1,NULL); pthread_create(&thid2,NULL,thread2,NULL); pthread_join(thid1,&statue); printf("statue1=%d\n",statue); pthread_join(thid2,&statue); printf("statue2=%d\n",statue); printf("main thread exit\n"); return 0; }

程序运行结果: 每次运行的会不一样。因为线程的调度是受系统控制的,不是按顺序调度的。一个有趣的现象是 当数据变量是3,为什么输出是2呢?因为读线程正准备打印2的时候被中断了,然后执行了写线程写入3。所以,互斥锁做的同步是保证加锁的部分所用到的资源,在同一时刻只有一个线程能用到。如果把输出语句放到加锁的部分,就可以看到读出来的和写出来的,是按顺序出来的。

2.条件变量 测试代码

#include <stdio.h> #include <unistd.h> #include <pthread.h> pthread_mutex_t mutex; pthread_cond_t cond; void * thread1(void *arg) { pthread_cleanup_push(pthread_mutex_unlock,&mutex); while(1) { printf("thread1 is running\n"); pthread_mutex_lock(&mutex); printf("thread1 mutex\n"); pthread_cond_wait(&cond,&mutex); printf("thread1 applied the condition\n"); pthread_mutex_unlock(&mutex); sleep(4); } pthread_cleanup_pop(0); } void *thread2(void *arg) { while(1) { printf("thread2 is running\n"); pthread_mutex_lock(&mutex); printf("thread2 mutex\n"); pthread_cond_wait(&cond,&mutex); printf("thread2 applied the condition\n"); pthread_mutex_unlock(&mutex); sleep(1); } } int main() { pthread_t tid1,tid2; printf("condition variable study\n"); pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cond,NULL); pthread_create(&tid1,NULL,(void *)thread1,NULL); pthread_create(&tid2,NULL,(void *)thread2,NULL); do{ pthread_cond_signal(&cond); }while(1); sleep(50); pthread_exit(0); }

上面代码加了“pthread_cleanup_push(pthread_mutex_unlock,&mutex);”和“pthread_cleanup_pop(0);”这对函数,目的是解决线程结束时资源释放的问题。因为pthread_cond_wait函数属于“取消点”。其实对于thread2也应该设置这对函数。他们在线程出现意外的时候,才会使用到。具体,请看我另一篇关于“线程终止的博文”

3.异步信号

六、错误码

错误码定义在“error.h”以E开头的宏定义。 常见的错误码

错误码含义ENOMEM内存不足EIO输入输出错误

最新回复(0)