Linux C 线程基础及多线程相关基础函数理解

本文介绍了Linux环境下C语言的线程基础,包括线程的概念、资源分配,以及线程创建、结束等API函数的使用。同时,详细讲解了线程同步的信号量机制和线程互斥的概念,帮助理解多线程编程中的关键概念和技术。

摘要:

         本次内容是关于Linux C下的线程基础以及多线程中相关函数的用法,并且都有一些简单的例子来帮助大家理解。

目录

1、线程基础

概念

线程的共有资源和私有资源

线程相关api函数(系统调用)

创建线程

线程收尸

结束当前进程

结束另一个线程的执行

获取线程的ID

设置线程的属性

2、线程同步和互斥

同步----信号量

线程互斥


       

1、线程基础

概念

        线程在进程的空间中的一个任务,对应一个线程函数,它是线程函数的一次执行过程。

线程的共有资源和私有资源

1、对于一个进程中的多个线程,以下为线程公有资源:

        可执行的指令(.text)

        静态数据(.rodata,.data,.bss)

        进程中打开的文件描述符

        信号处理函数

        当前工作目录

        用户ID

        用户组ID

2、线程私有资源 :

        线程ID (TID)

        PC(程序计数器)和相关寄存器

        堆栈

        局部变量

        返回地址

        错误号 (errno)

        信号掩码和优先级

        执行状态和属性

线程相关api函数(系统调用)

创建线程

#include <pthread.h>
Compile and link with -pthread.
       
int pthread_create(pthread_t *thread,
                     const pthread_attr_t *attr,
                     void *(*start_routine) (void *), 
                     void *arg);
//参数1 ----- 线程ID的指针
//参数2 ----- 线程的属性,默认属性:NULL
//参数3 ----- 线程要执行的函数(线程函数),必须按下面的格式定义
	void*  xxx(void* arg)
	{
		......
	        ......
	}
//参数4 ----- 传给线程的参数
//返回值 --- 成功:0,失败: 错误码
		
例如: 
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

//子线程
void* fun(void*arg)
{
	int i;
	for(i = 0; i < 7; i++){
		printf("this is a function!\n");
		sleep(1);
	}

	return 0;
}

int main(int argc,char **argv)
{
	int i;
	pthread_t tid;

	//fun();
	//创建线程

	if(pthread_create(&tid,NULL,fun,NULL)){
		perror("pthread_create");
		exit(1);
	}
	for(i = 0; i < 7; i++){
		printf("this is a main!\n");
		sleep(1);
	}
	return 0;
	}

//给线程函数传递参数 
例如: 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

struct student{
	int sno;
	char name[20];
	float score;
};

//子线程
void* fun(void*arg)
{
	struct student * p = (struct student*)arg;
	printf("%d %s %.2f\n",p->sno,p->name,p->score);
	//printf("%d\n",*(int*)arg);
	//printf("%s\n",(char*)arg);
	return 0;
}

int main(int argc,char **argv)
{
	pthread_t tid;
	//int a = 120;
	//char str[] = "hello world";
	struct student st = {1001,"jack",98.4};

	//创建线程
	//if(pthread_create(&tid,NULL,fun,&a)){
	//if(pthread_create(&tid,NULL,fun,str)){
	if(pthread_create(&tid,NULL,fun,&st)){
		perror("pthread_create");
		exit(1);
	}
	sleep(1);
	return 0;
}

线程收尸

//如果线程没有结束,则会使调用的线程阻塞,直到被收尸的线程结束
int pthread_join(pthread_t thread, void **retval);
//参数1 ----- 被收尸的线程ID
//参数2 ----- 保存线程返回值的变量的地址
//返回值 ---- 成功:0,失败:错误码 
例如: 
struct student{
	int sno;
	char name[20];
	float score;
};

//子线程
void* fun(void*arg)
{
	static int a = 120;
	struct student * p = (struct student*)arg;
	printf("%d %s %.2f\n",p->sno,p->name,p->score);
	//printf("%d\n",*(int*)arg);
	//printf("%s\n",(char*)arg);
	return &a;
}

int main(int argc,char **argv)
{
	pthread_t tid;
	//int a = 120;
	//char str[] = "hello world";
	struct student st = {1001,"jack",98.4};

	//创建线程
	//if(pthread_create(&tid,NULL,fun,&a)){
	//if(pthread_create(&tid,NULL,fun,str)){
	if(pthread_create(&tid,NULL,fun,&st)){
		perror("pthread_create");
		exit(1);
	}

	int *p;
	//使主线程挂起,给子线程收尸
	if(pthread_join(tid,(void**)&p)){
	//if(pthread_join(tid,NULL)){
		perror("pthread_join");
		exit(1);
	}
	printf("*p = %d\n",*p);

	return 0;   //exit(0)
}

结束当前进程

#include <pthread.h>
void pthread_exit(void *retval);
//参数 ---- 返回给主线程的数据
例如: 
//子线程
void* fun(void*arg)
{
	int i;
	static int a = 120;
	for(i = 0; i <10; i++){
	        printf("我是一个子线程!\n");
		sleep(1);
	}
	//exit(0);  //结束进程
#if 0
	return 0;   //结束线程的执行  = pthread_exit(0);
#else
	pthread_exit(&a);  //结束线程的执行
#endif
	}

int main(int argc,char **argv)
{
	pthread_t tid;
	int i;
	//创建线程
	if(pthread_create(&tid,NULL,fun,NULL)){
	        perror("pthread_create");
		exit(1);
	}

	int *p;
	if(pthread_join(tid,(void**)&p)){
		perror("pthread_join");
		exit(1);
	}
	printf("*p = %d\n",*p);
	for(i = 0; i <10; i++){
		printf("我是main!\n");
		sleep(1);
	}
#if 0
	return 0;   //调用exit(0),结束进程
#else
        pthread_exit(0);   //结束线程,如果主线程结束了,还有其他线程,则等所有线程结束进程才会结束
#endif
}

结束另一个线程的执行

int pthread_cancel(pthread_t thread);
//参数  ------- 要结束的线程的ID
//返回值 ------ 成功:0,失败:错误码
		
例如: 
//子线程
void* fun(void*arg)
{
	int i;
	for(i = 0; i <10; i++){
		printf("我是一个子线程!\n");
		sleep(1);
	}
	pthread_exit(0);  //结束线程的执行
}

int main(int argc,char **argv)
{
	pthread_t tid;
	int i;
	//创建线程
	if(pthread_create(&tid,NULL,fun,NULL)){
		perror("pthread_create");
		exit(1);
	}

	for(i = 0; i <10; i++){
		printf("我是main!\n");
		sleep(1);
		if(i == 5)
		pthread_cancel(tid);  //结束子线程的执行
	}
	pthread_exit(0);   //结束线程,如果主线程结束了,还有其他线程,则等所有线程结束进程才会结束
}

获取线程的ID

pthread_t pthread_self(void);  //返回调用线程的ID 
		
例如 : 
//子线程
void* fun(void*arg)
{
	printf("子线程ID:%ld\n",pthread_self());
	return 0;
}

int main(int argc,char **argv)
{
	pthread_t tid;

	//创建线程
	if(pthread_create(&tid,NULL,fun,NULL)){
		perror("pthread_create");
		exit(1);
	}

	//使主线程挂起,给子线程收尸
	if(pthread_join(tid,NULL)){
		perror("pthread_join");
		exit(1);
	}
	printf("主线程ID:%ld\n",pthread_self());
	return 0;
}

设置线程的属性

当创建线程时,通常需要设置线程的属性,属性分为两种:

        接合属性(默认属性): 该线程结束时,不会自己释放资源,需要被主线程回收资源

        分离属性:该线程结束时,它可以自己释放资源,此时,不再需要主线程回收资源

设置线程属性有两种方法:

方法一:通过函数设置: 
int pthread_detach(pthread_t thread);
//参数 ----- 线程的ID
//返回值 ---成功:0,失败:错误码
例如: 
//子线程
void* fun(void*arg)
{
	int i;
	for( i = 0; i < 4;i++){
		printf("子线程ID:%ld\n",pthread_self());
		sleep(1);
	}
	return 0;
}

int main(int argc,char **argv)
{
	pthread_t tid;

	//创建线程
	if(pthread_create(&tid,NULL,fun,NULL)){
		perror("pthread_create");
		exit(1);
	}
	//设置线程属性为分离属性
	if(pthread_detach(tid)){
		perror("pthread_detach");
		exit(1);
	}

	//使主线程挂起,给子线程收尸
	if(pthread_join(tid,NULL)){  //当线程为分离属性时,不需要再调用pthread_join()
		perror("pthread_join");
		//exit(1);
	}
	printf("主线程ID:%ld\n",pthread_self());
	pthread_exit(0);
}	

方法二:在创建线程时,直接设置为分离属性 
int pthread_attr_init(pthread_attr_t *attr);  //初始化线程属性变量
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);  //设置线程属性 
//参数1 --- 线程属性变量的地址
//参数2 --- 属性:
	PTHREAD_CREATE_DETACHED   -----分离属性 
        PTHREAD_CREATE_JOINABLE   -----接合属性 
						
例如: 
int main(int argc,char **argv)
{
	pthread_t tid;
	pthread_attr_t attr;

	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

	//创建线程
	if(pthread_create(&tid,&attr,fun,NULL)){
	//if(pthread_create(&tid,NULL,fun,NULL)){
		perror("pthread_create");
		exit(1);
	}

	//使主线程挂起,给子线程收尸
	if(pthread_join(tid,NULL)){  //当线程为分离属性时,不需要再调用pthread_join()
		perror("pthread_join");
		//exit(1);
	}
	printf("主线程ID:%ld\n",pthread_self());
	pthread_exit(0);
}

2、线程同步和互斥

同步----信号量

同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情

        信号量代表某一类资源,其值表示系统中该资源的数量

        信号量是一个受保护的变量,只能通过三种操作来访问

初始化

P操作(申请资源)

        P(S) 含义如下:

                        if (信号量的值大于0) { 申请资源的任务继续运行;信号量的值减一;}

                        else { 申请资源的任务阻塞;}

V操作(释放资源)

        V(S) 含义如下:

                        if (没有任务在等待该资源) { 信号量的值加一;}

                        else { 唤醒第一个等待的任务,让其继续运行}

                        信号量的值为非负整数

int  sem_init(sem_t *sem,  int pshared,  unsigned int value);    
//参数1 ---- 信号量的地址 
//参数2 ---- 权限: 0--线程之间,非0---进程之间
//参数3 ---- 信号量的初始值
	
int sem_wait(sem_t *sem);   //  P操作
int sem_post(sem_t *sem);  // V操作 
	
例如: 
	
sem_t sem;
char buf[100];

//子线程
void* fun1(void*arg)
{
	while(1){
		printf("请输入字符串:");
		fgets(buf,sizeof(buf),stdin);
		sem_post(&sem); //执行v操作
	}
	return 0;
}
void* fun2(void*arg)
{
	while(1){
		sem_wait(&sem); //执行p操作
		printf("%s",buf);
		sleep(1);
	}
	return 0;
}

int main(int argc,char **argv)
{
	pthread_t tid1,tid2;

	//初始化信号量
	sem_init(&sem,0,0);
	//创建线程
	if(pthread_create(&tid1,NULL,fun1,NULL)){
		perror("pthread_create");
		exit(1);
	}
	if(pthread_create(&tid2,NULL,fun2,NULL)){
		perror("pthread_create");
		exit(1);
	}

	//等待线程结束
	if(pthread_join(tid1,NULL)){
		perror("pthread_join");
		exit(1);
	}
	if(pthread_join(tid2,NULL)){
		perror("pthread_join");
		exit(1);
	}


	return 0;
}

线程互斥

引入互斥(mutual exclusion)的目的是用来保证共享数据操作的完整性

        互斥锁主要用来保护临界资源

        每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源

        线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止

//初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                        const pthread_mutexattr_t *restrict attr);  //动态初始化
//参数1 ----- 互斥锁的指针
//参数2 ----- 互斥锁属性,一般为NULL
	 
 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;   //静态 初始化
//上锁 
int pthread_mutex_lock(pthread_mutex_t *mutex);
//解锁  
int pthread_mutex_unlock(pthread_mutex_t *mutex);

例如: 
pthread_mutex_t mutex;
int a,b;

//子线程
void* fun(void*arg)
{
        int i;
        for( i = 0; ; i++){
	    pthread_mutex_lock(&mutex);  //上锁
	    a = i;
	    b = i;
	    pthread_mutex_unlock(&mutex); //解锁
	}
	   return 0;
}
int main(int argc,char **argv)
{
	pthread_t tid;

	pthread_mutex_init(&mutex,NULL);

	//创建线程
	if(pthread_create(&tid,NULL,fun,NULL)){
		perror("pthread_create");
		exit(1);
	}

	while(1){
		pthread_mutex_lock(&mutex);
		if(a != b){
			printf("a = %d,b = %d\n",a,b);
		}
		pthread_mutex_unlock(&mutex);
	}

	if(pthread_join(tid,NULL)){
		perror("pthread_join");
		exit(1);
	}

	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值