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

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

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

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

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

一、创建线程

在这里插入图片描述
线程常用函数

函数说明
pthread_t pthread_self(void)获取本线程的线程ID
int 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值