Linux——线程使用及互斥量

本文详细介绍了线程的基本操作,包括线程的创建、终止、等待和分离,以及如何通过互斥量解决线程间资源共享的问题。通过实例演示了线程互斥的重要性,并展示了如何正确使用互斥量来防止资源竞争。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程的基本操作

概念
线程是程序中的一个执行路线。每个程序当中至少有一个线程。
程序在执行的过程中是逐条执行的,按照代码的逻辑一次向下执行,所以无法同时完成两条指令,故而引进了线程,举个很简单的例子,如果同时进行两个死循环,用单线程的话只能进行一个死循环,另一个死循环永远也不会执行,故而用多线程就可以解决这个问题。在学习网络cs模型时更能体现线程的作用,因为你需要在发送数据的同时接收数据。

创建线程

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
	参数
 		thread:返回线程ID
 		attr:设置线程的属性,attr为NULL表示使用默认属性
 		start_routine:是个函数地址,线程启动后要执行的函数
 		arg:传给线程启动函数的参数
	返回值:成功返回0;失败返回错误码	

获取线程ID

	pthread_t pthread_self(void)
	返回值
		返回调用该函数的线程的ID

线程终止

void pthread_exit(void *value_ptr);
参数
	value_ptr:value_ptr不要指向一个局部变量。
该函数无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

int pthread_cancel(pthread_t thread);
参数
	thread:线程ID
返回值:成功返回0;失败返回错误码

线程等待

int pthread_join(pthread_t thread, void **value_ptr);
参数
 	thread:线程ID
 	value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码

为何要线程等待?线程退出时,其占有的空间资源并不会释放掉,在次创建一个线程时该空间资源并不会被再次利用,也就是说会造成内存泄漏,故而需要线程等待。其中线程等待函数中的参数value_ptr指向线程IDthread的线程返回值。如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED;如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。

线程分离

int pthread_detach(pthread_t thread);
参数
	thread:线程ID
返回值:成功返回0,错误返回错误号

线程分离与线程等待很相似,他们都能释放掉已结束线程占用的空间资源,但线程分离不关心线程退出的返回值,只要线程退出,就自动释放线程资源。

编译指令

gcc 123.c -o out -lpthread
注:编译多线程的程序一定要加上 -lpthread

示例:

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
void *fun(void *num)
{
	int i=0;
	for(;i<30;i++)
	{
		printf("a");
		if(i==25)
		{
		    pthread_exit(NULL);//输出25个a后线程终止
		}
		usleep(1);//延时1us
	}
}

int main()
{

	pthread_t ptid;
	if(pthread_create(&ptid,NULL,fun,NULL)!=0)//创建线程
	{
		return 0;
	}
	int i=0;
	for(;i<10;i++)
	{
	    if(i%2==0)
	    {
			printf("b");
	    }
	    usleep(2);//延时2us
	}
	printf("\n");
	pthread_join(ptid,NULL);线程等待

	return 0;
}

结果
在这里插入图片描述
主线程在执行5个b后,输出结束,此时等待子线程结束,即第二行结果显示,至于第一行为什么a、b都有输出,是因为线程在执行时是同时进行的,主线程输出一个b后睡眠2us,在这段时间子线程开始输出a,因为子线程也有1us睡眠,限制其执行速度,当主线程的2us时间到后,又开始主线程输出b。

线程互斥

我们先看看几个概念:

临界资源:多线程执行流共享的资源就叫做临界资源
临界区:每个线程内部,访问临界资源的代码,就叫做临界区
互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作
用
原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

那到底什么是线程互斥呢?不要着急,我们先看看下面这个抢票的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>

int ticket = 100;//共有100张票

void *route(void *arg)
{
	char *id=(char *)arg;
	while(1)
	{
		if(ticket>0)
		{
			usleep(1000);
			printf("%s sells ticket:%d\n",id,ticket);
			ticket--;//抢到票就减1
		}
		else
		{
			break;		
		}
	}
}
int main()
{
	pthread_t t1,t2,t3,t4;
	pthread_create(&t1,NULL,route,(void *)"thread 1");
	pthread_create(&t2,NULL,route,(void *)"thread 2");
	pthread_create(&t3,NULL,route,(void *)"thread 3");
	pthread_create(&t4,NULL,route,(void *)"thread 4");
	pthread_join(t1,NULL);
	pthread_join(t2,NULL);
	pthread_join(t3,NULL);
	pthread_join(t4,NULL);
	return 0;
	
}

结果
在这里插入图片描述
结果让人感到意外,怎么会有负数呢?因为线程是同时进行的,当进行到票数为1的时候,此时部分线程已经经过了**if(ticket>0)**的判断,其中有一个线程的执行速度比较快,他抢先拿到了最后的一张票,此时票数为0,因为此时并不止这一个线程经过了判断,他们也能强到一张票,故而出现负数。原理图如下:
在这里插入图片描述

为避免上述现象的出现,引进了互斥量mutex

互斥量初始化

静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
	参数:
 	mutex:要初始化的互斥量
 	attr:NULL

互斥量加锁与解锁

加锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);

解锁:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
注:加锁后一定要记得解锁

二者的参数相同,都是指初始化后的互斥量

互斥量销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:需要销毁的互斥量
注:静态分配的互斥量不需要销毁,已经枷锁的互斥量不能销毁

使用方法

  1. 在使用前先初始化
  2. 访问临界资源前加锁
  3. 访问临界资源后解锁
  4. 互斥量使用结束后销毁

改进后代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>

int ticket = 100;//票
pthread_mutex_t mutex;//定义互斥量

void *route(void *arg)
{
	char *id=(char *)arg;
	while(1)
	{
		pthread_mutex_lock(&mutex);//加锁
		if(ticket>0)
		{
			printf("%s sells ticket:%d\n",id,ticket);
			ticket--;
			pthread_mutex_unlock(&mutex);//解锁
		}
		else
		{
			pthread_mutex_unlock(&mutex);//解锁
			break;
			
		}
		usleep(2);
	}
}
int main()
{
	pthread_t t1,t2,t3,t4;
	pthread_mutex_init(&mutex,NULL);//初始化互斥量
	pthread_create(&t1,NULL,route,(void *)"thread 1");
	pthread_create(&t2,NULL,route,(void *)"thread 2");
	pthread_create(&t3,NULL,route,(void *)"thread 3");
	pthread_create(&t4,NULL,route,(void *)"thread 4");
	pthread_join(t1,NULL);
	pthread_join(t2,NULL);
	pthread_join(t3,NULL);
	pthread_join(t4,NULL);
	pthread_mutex_destroy(&mutex);//互斥量销毁
	return 0;
	
}

结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值