Linux系统编程:多线程及互斥量的使用


1️⃣线程概述

✨✨✨

  • 进程是CPU分配资源的最小单位,线程是CPU调度的最小单位
  • 每个进程都拥有自己的数据段、代码段和堆栈段,这就造成进程在进行创建、切换、撤销操作时,*需要较大的系统开销。为了减少系统开销,从进程中演化出了线程。
  • 为了让进程完成一定的工作,进程必须至少包含一个线程(主线程0)。
  • 线程存在于进程中,共享进程的资源(线程存放在栈区)
  • 线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。所以线程间拥有更方便的通信机制
    在这里插入图片描述

2️⃣pthread库

✨✨✨

  • POSIX 标准定义了一套线程操作相关的函数, 即pthread 库函数,用于让程序员更加方便地操作管理线程,函数名都是以前缀 pthread_ 开始,使用时要包含 <pthread.h>,而且在链接的时候要手动链接 pthread 这个库,如:gcc a.c -lpthread -o a.out。
  • pthread 库中多线程开发的最基本概念主要包含三点:线程,互斥锁,条件,涉及的API:
对象操作API
线程创建pthread_create
线程退出pthread_exit
线程等待pthread_join
互斥锁创建pthread_mutex_init
互斥锁销毁pthread_mutex_destroy
互斥锁加锁pthread_mutex_lock
互斥锁解锁pthread_mutex_unlock
条件创建pthread_cond_init
条件销毁pthread_cond_destroy
条件触发pthread_cond_signal
条件广播pthread_cond_broadcast
条件等待pthread_cond_wait/pthread_cond_timedwait

3️⃣线程的创建、等待和退出

✨✨✨

API(头文件pthread.h):

  • ❤️获取线程号

    pthread_t pthread_self(void);
    
    • 功能: 获取线程号

    • 参数: 无

    • 返回值: 调用线程的线程 ID (posix描述的线程ID),不会失败

  • ❤️线程号比较

    int pthread_equal(pthread_t t1, pthread_t t2);
    
    • 功能: 判断线程号 t1 和 t2 是否相等。为了方便移植,尽量使用函数来比较线程 ID。

    • 参数: t1,t2:待判断的线程号。

    • 返回值: 相等返回非0 ;不相等返回0

  • ❤️线程创建

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                       void *(*start_routine) (void *), void *arg);
    
    • 功能: 创建一个线程。

    • 参数:

      • thread:存放线程标识符。
      • attr:用于定制各种不同的线程属性,设置为NULL就是创建默认属性的线程。
      • start_routine:线程函数的入口地址。
      • arg:传给线程函数的参数。
    • 返回值: 成功返回0,否则返回错误编号

  • ❤️等待并回收线程资源

    int pthread_join(pthread_t thread, void **rval_ptr);
    
    • 功能: 等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。如果线程已经结束,那么该函数会立即返回。

    • 参数:

      • thread:被等待的线程号。
      • retval:用来存储线程退出状态的指针的地址。
    • 返回值:成功返回0,否则返回错误编号

  • ❤️线程分离

    int pthread_detach(pthread_t thread);
    
    • 功能: 使调用线程与当前进程分离,分离后不代表此线程不依赖与当前进程,线程分离的目的是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程结束之后,系统会自动回收它的资源。所以,此函数不会阻塞。(进程没有此机制)

    • 参数: thread 线程号

    • 返回值: 成功返回0,否则返回错误编号

  • ❤️线程退出

    void pthread_exit(void *retval);
    
    • 功能: 退出调用线程。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放。

    • 参数: retval 存储线程退出状态的指针。

    • 返回值: 无

代码示例:

  • th.c:

    #include<stdio.h>
    #include<pthread.h>
    
    void *start_rtn (void *arg)
    {
    	static int t1Exit = 10;    //线程返回值,注意为static类型
    	printf("this is thread:%ld\n",(unsigned long)pthread_self());
    	printf("th data:%s\n",(char*)arg);
    	pthread_exit((void *)&t1Exit); //线程退出
    }
    
    	
    int main()
    {
    	pthread_t th = 0;
    	char *data ="NB 666 My Baby!";
    
    	if(pthread_create(&th,NULL,start_rtn,(void*)data) != 0){
    		perror("thread error");
    	}	
    
    	int* thret;
    	pthread_join(th,(void**)thret); //主线程阻塞等待线程th退出
    	printf("th return:%d\n",thret); //打印线程退出值
    
    	return 0;
    }
    
    

4️⃣互斥锁mutex

🔒✨✨✨🔑

  • 互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即🔒加锁( lock )和🔑解锁( unlock )。
  • 举个例子来说就是好几个人都想上厕所🚽,但是厕所一次仅供一人享用,要想保证上厕所过程不被打断进去就要把门锁上🔒,对于其他人来说要一直等到门开锁🔑才能进去。
  • 互斥锁的操作流程
    • 🔒在访问共享资源后临界区域前,对互斥锁进行加锁。
    • 🔑在访问完成后释放互斥锁上的锁。
    • 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。
    • 互斥锁的数据类型是:pthread_mutex_t

API(头文件pthread.h):

  • ❤️互斥锁初始化

    • 动态初始化:

      int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
      
      • 功能: 初始化一个互斥锁。
      • 参数:
      • mutex:互斥锁地址。类型是 pthread_mutex_t 。
      • attr:设置互斥量的属性,NULL未默认属性
      • 返回值:成功:0,成功申请的锁默认是打开的;失败返回错误码
    • 静态初始化:

    • 可以使用宏 PTHREAD_MUTEX_INITIALIZER (只对静态分配的互斥量)静态初始化互斥锁

      pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
      
  • 🔒互斥锁加锁

    int pthread_mutex_lock(pthread_mutex_t *mutex);
    
    • 功能: 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。

    • 参数: mutex:互斥锁地址。

    • 返回值:成功0,否则返回错误码

  • 🔑互斥锁解锁

    int pthread_mutex_unlock(pthread_mutex_t * mutex);
    
    • 功能: 对指定的互斥锁解锁。

    • 参数: mutex:互斥锁地址。

    • 返回值:成功0,否则返回错误码

  • 💔互斥锁销毁

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    
    • 功能: 销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。

    • 参数: mutex 互斥锁地址。

    • 返回值:成功0,否则返回错误码

示例代码

  • mutex.c:

    #include<stdio.h>
    #include<pthread.h>
    
    pthread_mutex_t mutex;
    
    void *start_rtn1 (void *arg)
    {
    	int i=5;
    	pthread_mutex_lock(&mutex);
    	while(i--){
    		printf("this is thread1:%ld\n",(unsigned long)pthread_self());
    		printf("data:%d\n",(*(int*)arg)++);
    	}
    	pthread_mutex_unlock(&mutex);
    }
    
    
    void *start_rtn2 (void *arg)
    {
    	int i=5;
    	pthread_mutex_lock(&mutex);
    	while(i--){
    		printf("this is thread2:%ld\n",(unsigned long)pthread_self());
    		printf("data:%d\n",(*(int*)arg)++);
    	}
    	pthread_mutex_unlock(&mutex);	
    }
    
    int main()
    {
    	pthread_t th1 = 0;
    	pthread_t th2 = 0;
    	pthread_mutex_init(&mutex,NULL);
    	int data = 0;
    	
    	if(pthread_create(&th1,NULL,start_rtn1,(void*)&data) != 0){
    		perror("thread1 error");
    	}
    
    		
    	if(pthread_create(&th2,NULL,start_rtn2,(void*)&data) != 0){
    		perror("thread2 error");
    	}	
    
    	
    	pthread_join(th1,NULL);
    	pthread_join(th2,NULL);
    
    	pthread_mutex_destroy(&mutex);
    
    	return 0;
    }
    
  • 终端显示:

在这里插入图片描述


5️⃣条件变量

  • 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
  • 条件变量主要包括两个动作:一个线程等待“条件变量的成立”,条件不成立时线程挂起;另一个线程修改条件,并判断条件是否成立,当“条件成立”时,唤醒等待条件成立的线程。

API(头文件pthread.h):

  • ❤️条件变量初始化

    • 动态初始化:

      int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
      
      • 功能: 初始化一个条件变量。
      • 参数:
        • cond:指向条件变量,条件变量用pthread_cond_t数据类型表示
        • attr:除非需要创建一个非默认属性的条件变量,否则attr参数可以设置为NULL。
      • 返回值:成功0,否则返回错误码
    • 静态初始化:

    • 把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量

      pthread_cond_t cond = PTHREAD_COND_INITIALIZER
      
  • ⌚️等待

    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
    int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);
    
    • 功能:
      • pthread_cond_wait:当条件不到来时,一直阻塞
      • pthread_cond_timedwait:当条件不到来时,仅仅只阻塞一定的时长
    • 参数:
      • cond:创建的条件参数
      • mutex:创建的线程参数
      • timeout:指定等待的时间(timedwait)
    • 返回值:成功0,否则返回错误码
  • 📣 触发和广播

    //触发
    int pthread_cond_signal(pthread_cond_t *cond);
    //广播
    int pthread_cond_broadcast(pthread_cond_t *cond);
    
    • 触发表示唤醒任意一个等待该条件的线程,而广播表示唤醒所有等待该条件的线程。
    • 参数:cond 条件变量
    • 返回值:成功0,否则返回错误码
  • 💔条件变量的销毁

    int pthread_cond_destroy(pthread_cond_t *cond);
    
    • 功能: 销毁指定的一个条件变量

    • 参数: cond 条件变量

    • 返回值:成功0,否则返回错误码

示例代码:

  • 生产者——消费者模型cond.c:

    #include <stdio.h>
    #include <pthread.h>
     
    #define MAX 5
     
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t notfull = PTHREAD_COND_INITIALIZER; 	//是否队满
    pthread_cond_t notempty = PTHREAD_COND_INITIALIZER; 	//是否队空
     
    int top = 0;
    int bottom = 0;
     
    void* produce(void* arg)
    {
    	int i;
    	for ( i = 0; i < MAX*2; i++)
    	{
    		pthread_mutex_lock(&mutex);
    		while ((top+1)%MAX == bottom)
    		{
    			printf("full! producer is waiting\n");
    			pthread_cond_wait(&notfull, &mutex);//等待队不满
    		}
     
    		top = (top+1) % MAX;
    		printf("now top is %d\n", top);
    		pthread_cond_signal(&notempty);//发出队非空的消息
     
    		pthread_mutex_unlock(&mutex);
    	}
    	return (void*)1;
    }
     
    void* consume(void* arg)
    {
    	int i;
    	for ( i = 0; i < MAX*2; i++)
    	{
    		pthread_mutex_lock(&mutex);
    		while ( top%MAX == bottom)
    		{
    			printf("empty! consumer is waiting\n");
    			pthread_cond_wait(&notempty, &mutex);//等待队不空
    		}
    		bottom = (bottom+1) % MAX;
    		printf("now bottom is %d\n", bottom);
    		pthread_cond_signal(&notfull);//发出队不满的消息
     
    		pthread_mutex_unlock(&mutex);
    	}
     
    	return (void*)2;
    }
     
    int main(int argc, char *argv[])
    {
    	pthread_t thid1;
    	pthread_t thid2;
    	pthread_t thid3;
    	pthread_t thid4;
     
    	int  ret1;
    	int  ret2;
    	int  ret3;
    	int  ret4;
     
    	pthread_create(&thid1, NULL, produce, NULL);
    	pthread_create(&thid2, NULL, consume, NULL);
    	pthread_create(&thid3, NULL, produce, NULL);
    	pthread_create(&thid4, NULL, consume, NULL);
     
    	pthread_join(thid1, (void**)&ret1);
    	pthread_join(thid2, (void**)&ret2);
    	pthread_join(thid3, (void**)&ret3);
    	pthread_join(thid4, (void**)&ret4);
     
    	return 0;
    }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

money的大雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值