Linux线程

本文围绕Linux线程展开,介绍了进程和线程的概念、关系及优缺点。详细阐述线程创建的常用API,还说明了线程互斥和同步的重要性。互斥锁可确保共享资源安全访问,避免数据不一致;条件变量用于线程同步,控制线程先后执行顺序,解决并发问题。

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

Linux线程

进程的概念

前边说过由用户所编辑指令的c文件在没有运行起来的时候是一个程序,然后经过编译变成一个可执行文件(.exe / a.out),最终将可执行文件运行起来就变成了一个进程进程本身占用系统资源,当启动了进程以后,在用户空间中会占用数据段、代码段和堆栈段。所以进程对资源开销比较大。

举一个例子:12306在节假日的期间访问人数变多,所有的用户查询车次等操作会向12306这个系统发送请求,那么服务器相应完这个请求会给用户返回一个界面。如果服务器端对每一个请求都启动一个新的进程去处理的话,那么这个系统的开销是很庞大的。而且进程间相互切换也同样比较耗费资源,如果在用户非常庞大的情况下,使用进程去处理大并发的请求,那么这个设计是十分不合理的,容易导致系统的瘫痪。那么就引入线程来处理这种大并发的请求。

线程的概念

要点:进程资源管理的最小单位,线程程序执行的最小单位。

举个例子:现在假如创建了一个word,然后对新建的word进行编辑,保存等操作。如果这些操作都去启动一个进程去做的话,那显然不太现实,资源开销太大。实际上在word编辑的过程中是启动了多个线程去处理这些操作,进程只有新建word的这一个进程。

由此可见:线程是属于进程的,一个进程默认至少有一个线程,后续有多少个进程取决于用户后续的操作。可以说调用进程就工作,就是在启动线程去工作的。一个线程就是进程里的一个工作流,一个进程可以有多个工作流,所以线程是程序执行的最小单位。

每个进程都有自己的数据段、代码段和堆栈段线程隶属于某个进程,它包含独立的栈和CPU寄存器状态,但是它共享这个进程所有的用户空间,因为它本身属于这个进程。包括打开的文件、内存页面、信号标识及动态分配内存等

对于线程的资源开销几乎可以忽略不计,可以减小进程上下文切换的开销。

进程和线程的关系

线程是属于进程的,线程运行在进程空间内,同意进程产生的进程共享同一用户内存空间,当进程退出时该进程产生的线程都会被强制退出并清除。一个进程至少需要一个线程作为它的指令执行体,进程管理这资源(比如CPU、内存、文件等等),而将线程分配到某个CPU上执行。

当进程创建起来的时候默认只有一个线程(主线程),此时如果遇到大并发的请求,那么一个线程的处理效率就不够,需要创建其他的线程(子线程)来处理新的请求,这样能提高程序的执行效率。但不管是子线程还是主线程它们都隶属与这个进程,共享这个进程所有的用户空间。

image-20240129162615305

目前的操作系统都是多用户、多任务的分时操作系统。可以在一个系统中启动多个进程,进程都是抢占式的,轮流占有CPU资源,系统根据进程的优先级,调度进程由就绪状态变为运行状态,CPU会给进程分配一个时间片(时间片_百度百科 (baidu.com)),进程必须在这个时间片内运行完毕,如果没有运行完毕,系统会将这个进程阻塞,转而去执行别的进程。如果这个进程只有一个线程,那么时间片就是分配给这个线程去执行的;如果进程中包含多个线程,且同一时间只能运行一个线程,它们也同样要服从系统调度,也就是说多个线程中不会只执行一个线程,因为一个线程不可能永久占有CPU资源。系统调度会根据系统优先级去调度某一个线程去执行。但是现成的执行时间不会超过分配给整个进程的时间片。当分配给进程的时间片执行完毕后整个线程会被强制退出。

线程和进程的优缺点

线程

优点:

  • 轻量级:线程是操作系统调度的基本单位,相比进程,线程的创建、切换和销毁的开销更小,占用的系统资源更少;
  • 共享资源:多个线程可以共享一个进程的内存空间、文件描述符等,这样可以方便地进行数据共享和通信;
  • 响应性高:由于线程的轻量级特性,线程的创建和切换速度更快,可以更快的响应用户的请求,提高系统的交互性能;
  • 并发性:多个线程可以并发执行,从而可以实现并发处理,提高系统的效率。

缺点:

  • 容易出错:由于线程共享一个进程的资源,多个线程之间的数据共享需要进行同步和互斥操作,容易出现竞态条件和死锁等并发问题;
  • 缺乏独立性:线程之间共享一个进程的的地址空间,一个线程的错误可能会影响到其他线程和整个线程的稳定性。例如,如果一个线程因为段错误导致线程退出,那么整个进程就结束掉了。
  • 安全性问题:线程之间的共享资源需要进行合理的同步和互斥操作,负责可能导致数据不一致和安全性问题。例如,一个线程如果被阻塞,由别的线程去执行后转而回到原线程执行的位置执行,那么就可能会导致数据不一致等问题。(假如同步和互斥条件没有控制好)

进程

优点:

  • 独立性:每个进程拥有独立的地址空间和资源,一个进程的错误不会影响其他进程的稳定性;
  • 安全性:进程之间的资源是相互隔离的,不同进程之间的数据都不会相互干扰,提高了系统的稳定性。(例如之前使用父子进程对一个变量进行修改,父子进程都有自己独立的空间,它们之间修改的数据并不会相互影响)
  • 可靠性:进程之间的隔离性可以提高系统的稳定性,一个进程的崩溃并不会造成整个系统的崩溃;
  • 灵活性:进程可以独立的调度和管理,可以更好地实现任务的分配和资源的管理。

缺点:

  • 重量级:相比线程,进程的创建、切换和销毁的开销更大,占用的系统资源更多;
  • 通信复杂:不同进程之间的通信需要使用进程间通信(IPC)机制,如管道、消息队列、共享内存等,通信复杂度较高;
  • 响应性低:由于进程的重量级特性,进程的创建和切换速度较慢,响应性相对较低。

线程的创建

线程常用的API

线程创建

声明:pthread_create()函数用于创建一个新的线程

 #include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

参数

  • pthread_t *thread:指向一个pthread_t类型的指针,用于存储新创建线程的ID。调用者负责分配这个变量的存储空间,并确保它在pthread_create()返回后仍然有效,以便以后使用(例如,通过pthread_join()等待线程结束)。
  • const pthread_attr_t *attr是一个指向pthread_attr_t类型的指针,用来设置线程的属性。如果设置为NULL表示默认属性,包括堆栈地址、堆栈大小和调度策略等
  • void *(*start_routine) (void *)这是一个函数指针,指向新线程开始执行的函数。这个函数必须具有void *(*)(void *)的形式,即它接受一个void *类型的参数,并返回一个void *类型的指针。
  • void *arg:这是一个void *类型的指针,将被传递给start_routine函数作为其唯一的参数。如果你的线程函数不需要参数,可以传递NULL

返回值

  • 如果成功,pthread_create()将返回0。
  • 如果失败,将返回一个非零错误码。
线程退出

声明pthread_exit函数用于终止调用线程,并可以返回一个可选的值给等待该线程结束的线程

 #include <pthread.h>

void pthread_exit(void *retval);

参数

  • void *value:这是一个指向任意类型的指针,可以被设置为NULL。如果提供了非NULL的值,那么这个值将被作为线程的返回值传递给等待该线程的线程。

由于一个进程中的多个线程共享数据段,因此通常在线程退出后,退出线程所占有的资源并不会随着线程结束而释放。所有的资源都需要调用pthread_join函数来等待线程结束,类似于wait系统调用。

线程等待

声明:pthread_join函数用于等待一个线程的结束,并回收其资源

 #include <pthread.h>

 int pthread_join(pthread_t thread, void **retval);

参数

  • pthread_t thread,这是被等待线程的标识符,即线程ID。这个ID是在创建线程时通过pthread_create函数得到的。
  • void *(*status)(void *),这是一个用户定义的指针,可以用来存储被等待线程的返回值。如果这个指针不为NULL,那么线程的返回值(例如通过pthread_exit给出的值)将存储在这个指针指向的位置。

返回值

  • 如果成功,pthread_create()将返回0。
  • 如果失败,将返回一个非零错误码。

此外,调用pthread_join的线程将会被阻塞,直到指定的线程结束。这意味着,如果主线程中没有调用pthread_join来等待子线程结束,主线程可能会先于子线程结束,导致子线程没有机会执行或者在执行过程中被突然终止。

获取线程id

声明

 #include <pthread.h>

pthread_t pthread_self(void);

返回值

它返回调用线程的线程ID,即当前线程的唯一标识符。

示例:创建线程并使用
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

void* pthread_func(void *arg)
{
        printf("this is new create pthread\n");
        printf("new create pthread id is %lx\n",(unsigned long)pthread_self);
        printf("Get the parameter from the control pthread is %d\n",*((int *)arg));

        pthread_exit(NULL);
}

int main()
{
        int err;
        int parameter = 123;
        pthread_t tid;

        if((err = pthread_create(&tid,NULL,pthread_func,(void *)&parameter)) != 0)      //创建线程
        {
                perror("pthread_create");
                exit(EXIT_FAILURE);
        }

        printf("the control pthread id is %lx\n",(unsigned long)pthread_self());        //获取主控线程的id

        pthread_join(tid,NULL);         //阻塞被调用线程直到新创建的线程退出
		//如果这里不设置线程等待,那么主控线程就会先于新建线程结束,进而导致整个进程的退出
        return 0;
}

编译结果

image-20240201153034285

示例:线程的使用
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

struct Student
{
	int num;
	char name[20];
	char sex[4];
	int age;
	int score;
};

void* pthread_func1(void *arg)
{
	int i;
	static char *string1 = "pthread1 finished";	//这里一定要声明成静态变量,要不然线程执行结束,它里边的值也被清空

	printf("this is new thread1,id is %lx\n",(unsigned long)pthread_self());
	puts("print array:");

	for(i=0;i<5;i++)
	{
		printf("%d ",*((int *)arg+i));
	}
	printf("\n");
	printf("\n");
	pthread_exit(string1);
}

void* pthread_func2(void *arg)
{
	static char *string2 = "pthread2 finished";

	printf("this is new thread2,id is %lx\n",(unsigned long)pthread_self());

	printf("print string:\n");
	printf("%s\n",(char *)arg);
	printf("\n");
	pthread_exit(string2);
}

void* pthread_func3(void *arg)
{
	static char *string3 = "pthread3 finished";

	printf("this is new thread3,id is %lx\n",(unsigned long)pthread_self());

	printf("print structure:s\n");
	printf("num:%d name:%s sex:%s age:%d score:%d\n",((struct Student *)arg)->num,((struct Student *)arg)->name,((struct Student *)arg)->sex,((struct Student *)arg)->age,((struct Student *)arg)->score);
	printf("\n");
	pthread_exit(string3);
}


int main()
{
	int ret;
	pthread_t tid[3] = {0};
	int arr[5] = {11,22,33,44,55};
	char *str = "hello world";
	struct Student stu = {2001,"玛卡巴卡","男",18,110};
	char *pret,pret2,pret3;


	if((ret = pthread_create(&tid[0],NULL,pthread_func1,arr)) != 0)	
	{
		perror("pthread_create");
        exit(EXIT_FAILURE);
	}

	if((ret = pthread_create(&tid[1],NULL,pthread_func2,str)) != 0)	
	{
		perror("pthread_create");
        exit(EXIT_FAILURE);
	}

	if((ret = pthread_create(&tid[2],NULL,pthread_func3,&stu)) != 0)	
	{
		perror("pthread_create");
        exit(EXIT_FAILURE);
	}

	printf("main:the control thread id is %lx\n",(unsigned long)pthread_self());

	pthread_join(tid[0],(void **)&pret);
	printf("the pthread1 return value is %s\n",pret);

	pthread_join(tid[1],(void **)&pret);
	printf("the pthread2 return value is %s\n",pret);

	pthread_join(tid[2],(void **)&pret);
	printf("the pthread3 return value is %s\n",pret);

	printf("pthread is all finished,process quit\n");
	return 0;
}

编译结果

image-20240201162727487

上边的代码分别新建了三个线程来处理不同的事,线程1来打印数组,线程2来打印字符串,线程3来打印结构体。可见线程的使用可以减少资源的开销,而且互相通信也比进程方便。

示例:线程的共享资源
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
int g_data = 0;

void* pthread_func1(void *arg)
{
	while(1)
	{
		printf("t1: %d\n",g_data++);
		sleep(1);
	}
}

void* pthread_func2(void *arg)
{
	while(1)
	{
		printf("t2: %d\n",g_data++);
		sleep(1);
	}
}

int main()
{
	int err;
	int parameter = 123;
	pthread_t tid1,tid2;

	if((err = pthread_create(&tid1,NULL,pthread_func1,(void *)&parameter)) != 0)	//创建线程
	{
		perror("pthread_create");
		exit(EXIT_FAILURE);
	}

	if((err = pthread_create(&tid2,NULL,pthread_func2,(void *)&parameter)) != 0)	//创建线程
	{
		perror("pthread_create");
		exit(EXIT_FAILURE);
	}

	while(1)
	{
		printf("main: %d\n",g_data++);
		sleep(1);
	}
	
	pthread_join(tid1,NULL);		//阻塞被调用线程直到新创建的线程退出
	pthread_join(tid2,NULL);

	return 0;
}

编译结果

image-20240203114328438

上边的全局变量g_data就是一个共享资源,每一个线程都能访问这个资源,并且对这个资源进行修改。虽然极大的方便了线程之间的通信,但是这种方法访问数据的方式不安全,可以使用后边的互斥和同步来解决。(或者使用局部变量,尽量减少全局变量的使用)

线程的互斥

由以上的代码可知,当多个线程同时访问共享资源的时候会造成数据错乱等,所以要引入线程互斥的操作。

举个例子:当人们在ATM机取钱的时候,同一时间只能进去一个人取钱,如果多个人进去显然不合理,所以就要给ATM机上锁,当里边进去一个人操作的时候,别的人只能等待,同一时间只有一个人能对共享资源进行访问。同理对应到线程的共享资源也是这样。

互斥锁

互斥锁是一种用于保护共享资源的同步机制,它可以确保在任意时刻只有一个线程可以访问共享资源。当一个线程获得了互斥锁后,其他线程就无法再获得相同的锁,知道该线程释放了互斥锁。

互斥锁的工作原理:

  1. 当一个线程需要访问共享资源时,它首先尝试获取互斥锁;
  2. 如果互斥锁当前没有被任何线程占用,则该线程成功获取互斥锁,并可以访问共享资源;
  3. 如果互斥锁已经被其他线程占用,则当前线程将被阻塞,直到互斥锁被释放;
  4. 当线程访问完共享资源后,释放互斥锁,以允许其他线程获取互斥锁并访问共享资源。

通过互斥锁的工作原理,可以确保再多线程的环境下对共享资源的安全访问,避免了多个线程同时访问共享资源导致的数据不一致和竞态条件问题。

互斥锁常用API

互斥锁的常用API包括初始化、锁定、解锁和销毁等操作。

以下是一些互斥锁的基本操作函数:

  1. 初始化互斥锁:使用pthread_mutex_init函数来创建一个互斥锁。这个函数需要指定一个互斥锁对象以及一个可选的互斥锁属性对象。如果成功,函数返回0;否则返回错误码。

    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
    
  2. 静态或动态初始化:互斥锁可以在声明时通过PTHREAD_MUTEX_INITIALIZER进行静态初始化,或者在声明后使用pthread_mutex_init进行动态初始化。

  3. 锁定互斥锁:使用pthread_mutex_lock函数来锁定互斥锁。如果互斥锁已经被其他线程锁定,调用此函数的线程将会阻塞,直到互斥锁被释放。

    int pthread_mutex_lock(pthread_mutex_t *mutex);
    
  4. 解锁互斥锁:使用pthread_mutex_unlock函数来解锁互斥锁,允许其他等待该锁的线程获取锁。

    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
  5. 销毁互斥锁:使用pthread_mutex_destroy函数来销毁一个互斥锁。在线程结束前应确保互斥锁已被销毁,以避免资源泄露。

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    
  6. 尝试锁定互斥锁:使用pthread_mutex_trylock函数可以尝试锁定互斥锁,如果互斥锁当前未被锁定,则立即返回并持有锁;如果互斥锁已被锁定,则立即返回,不会导致调用线程阻塞。

    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    
示例:创建线程并使用互斥锁来完成对共享资源的控制
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

int g_data = 0;
pthread_mutex_t mutex;		//定义互斥锁类型


void *pthread_func1(void  *arg)
{
	pthread_mutex_lock(&mutex);		//加锁,对共享资源进行保护
	while(1)
	{
		printf("tid1: %d\n",g_data++);
		sleep(1);
		if(g_data == 3)
		{
			printf("tid1 quit==========\n");
			exit(0);
			pthread_mutex_unlock(&mutex);		//解锁,使得其他线程能够获取到这个锁
			pthread_exit(NULL);
		}
	}
}

void *pthread_func2(void *arg)
{
	while(1)
	{
		printf("tid2: %d\n",g_data);
		sleep(1);
		pthread_mutex_lock(&mutex);
		g_data++;
		pthread_mutex_unlock(&mutex);
	}
}

int main()
{
	pthread_t tid1,tid2;
	int  ret;

	pthread_mutex_init(&mutex,NULL);	//对互斥锁进行初始化

	if((ret = pthread_create(&tid1,NULL,pthread_func1,NULL)) == 0)
	{
		printf("main: create new thread successfully\n");
	}

	if((ret = pthread_create(&tid2,NULL,pthread_func2,NULL)) == 0)
	{
		printf("main: create new thread successfully\n");
	}

	while(1)
	{
		printf("main: %d\n",g_data);
		sleep(1);
	}
	pthread_mutex_destroy(&mutex);		//对互斥锁进行销毁

	return 0;
}

编译结果

image-20240321091302218

代码分析

首先主控线程创建了两个新的线程,然后在主控线程中打印g_data的值,此时g_data的值为0,主控线程休眠一秒,此时子进程2抢占到了CPU,执行子进程2的程序,子进程2同样打印了g_data的值,此时g_data的值没有发生变化。子进程2休眠一秒,子进程1抢占到CPU,此时执行子进程1的代码,子进程1首先加互斥锁,然后执行g_data++会先打印后会它的值加加,此时g_data的值已经变为1。子进程休眠一秒后被主控线程捕获,此时打印g_data的值为1,然后休眠一秒,此时可能子进程2抢占到了CPU,继续接着之前的代码执行,子进程2尝试去上锁,但此时这把锁已经被子进程1获取,所以子进程2就被阻塞,继而接着去执行子进程1的程序,直到子进程检测到g_data的值变为3后解锁并将整个进程退出。

死锁

死锁是指两个或多个执行单元(如进程、线程或任务)在等待彼此持有的资源时,导致它们都无法向前推进执行的一种现象。

产生死锁的条件

  • 互斥条件:至少有一个资源每次只能被一个执行单元使用。
  • 占有和等待条件:一个执行单元至少持有一个资源,并等待获取其他执行单元持有的资源。
  • 不剥夺条件:执行单元已获得的资源,在未使用完之前,不能被其他执行单元强行剥夺。
  • 循环等待条件:存在一种执行单元资源的循环等待链。

简单来说,死锁就是目前有两把锁,它们各自都想拿到对方的那把锁,但是拿不到最终导致线程一直阻塞的现象。

示例:死锁的产生
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

int g_data = 0;
pthread_mutex_t mutex;		//定义互斥锁类型
pthread_mutex_t mutex2;


void *pthread_func1(void  *arg)
{
	pthread_mutex_lock(&mutex);		//加锁,对共享资源进行保护
	sleep(1);
	pthread_mutex_lock(&mutex2);
	while(1)
	{
		printf("tid1: %d\n",g_data++);
		sleep(1);
		if(g_data == 3)
		{
			printf("tid1 quit==========\n");
			exit(0);
			pthread_mutex_unlock(&mutex);		//解锁,使得其他线程能够获取到这个锁
			pthread_exit(NULL);
		}
	}
}

void *pthread_func2(void *arg)
{
	pthread_mutex_lock(&mutex2);
	sleep(1);
	pthread_mutex_lock(&mutex);

	while(1)
	{
		printf("tid2: %d\n",g_data);
		sleep(1);
		pthread_mutex_lock(&mutex);
		g_data++;
		pthread_mutex_unlock(&mutex);
	}
}

int main()
{
	pthread_t tid1,tid2;
	int  ret;

	pthread_mutex_init(&mutex,NULL);	//对互斥锁进行初始化
	pthread_mutex_init(&mutex2,NULL);

	if((ret = pthread_create(&tid1,NULL,pthread_func1,NULL)) == 0)
	{
		printf("main: create new thread successfully\n");
	}

	if((ret = pthread_create(&tid2,NULL,pthread_func2,NULL)) == 0)
	{
		printf("main: create new thread successfully\n");
	}

	while(1)
	{
		printf("main: %d\n",g_data);
		sleep(1);
	}
	
	pthread_mutex_destroy(&mutex);		//对互斥锁进行销毁
	pthread_mutex_destroy(&mutex2);
	return 0;
}

编译结果

image-20240321111503543

代码分析:由于线程1对第一把锁进行上锁后,休眠一秒线程2抢占到了CPU,线程2对第二把锁进行上锁后,休眠一秒。然后回到刚刚线程1执行的位置,想要获取到第二把锁,而第二把锁已经被线程2获取,所以线程1处于阻塞状态。而线程2也想要去获取第一把锁,所以它也会被阻塞。由此造成了这两个线程都被阻塞不执行。

线程的同步

​ 线程的同步十一个宏观概念,在微观上包含着现成的相互排斥和线程先后执行的约束问题。

举一个例子,假设现在公司要研发一款新的产品,那么就会有研发组和测试组。而测试组只能基于研发组在完成的情况下对它的结果进行下一步的操作。也就是说在研发组正在执行的过程中,测试组是处于阻塞状态的。和之前的线程互斥不相同,线程的互斥是两个线程是相互竞争的关系,在程序执行的过程中,可能CPU会给不同的线程分配不同的时间片去执行它的程序。而线程的同步是其中的一个线程必须执行出某个结果后满足条件另外的线程才能执行。如果不满足条件,那么其他的线程只能处于阻塞状态,这是线程同步和互斥最大的区别。

条件变量

条件变量是一种线程间同步机制,它允许一个或多个线程等待直到另一个线程修改了共享数据并通知条件已改变

条件变量通常与互斥锁一起使用,以确保当线程在等待条件时,对共享数据的访问是受控的。

条件变量常用的API

条件变量的API主要包括初始化、等待、通知、销毁等操作

以下是关于条件变量的基本操作函数:

  1. 初始化条件变量:使用pthread_cond_init函数用来初始化条件变量.如果成功返回0,否则返回错误码。
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  1. 静态或动态初始化:互斥锁可以在声明时通过PTHREAD_COND_INITIALIZER进行静态初始化,或者在声明后使用pthread_cond_init进行动态初始化。
  2. 等待条件变量满足:线程可以在条件变量上等待,直到另一个线程通过某种方式改变了与之相关的条件,并通过信号机制通知等待的线程。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
  1. 唤醒等待线程:当条件得到满足时,可以使用特定的函数来唤醒等待在该条件变量上的一个或所有线程。
int pthread_cond_signal(pthread_cond_t *cond);	//唤醒等待在条件变量的一个线程
int pthread_cond_broadcast(pthread_cond_t *cond);	//唤醒等待在条件变量上的所有线程
  1. 销毁条件变量:释放它所占用的资源
int pthread_cond_destroy(pthread_cond_t *cond);
示例:使用条件变量对线程进行控制
#include "stdio.h"
#include "pthread.h"
#include "stdlib.h"
#include "unistd.h"

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;		//静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int g_data = 0;

void * pthread_fun1(void *parameter)
{

	while(1)
	{
		pthread_cond_wait(&cond,&mutex);
		printf("t1 run==========\n");
		g_data = 0;
		sleep(1);
	}
}

void * pthread_fun2(void *parameter)
{
	while(1)
	{
		printf("t2:%d\n",g_data);
		pthread_mutex_lock(&mutex);
		g_data++;
		if(g_data == 3)
		{
			pthread_cond_signal(&cond);
		}
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
}

int main()
{
	pthread_t tid1;
	pthread_t tid2;
	int data = 10;
	int ret;



	ret = pthread_create(&tid1,NULL,pthread_fun1,(void *)(&data));//第一个参数是新创建线程的id,第二个参数用于设置线程的属性,如果需要保持默认就是NULL
								      //第三个参数是新线程开始执行的函数,返回一个无类型指针,并且接受一个无类型指针作为参数
								      //第四个参数是主线程传给新建线程的唯一参数,如果不需要参数直接写NULL
	if(ret != 0)
	{
		printf("create pthread error\n");
		exit(EXIT_FAILURE);
	}
	ret = pthread_create(&tid2,NULL,pthread_fun2,(void *)(&data));//第一个参数是新创建线程的id,第二个参数用于设置线程的属性,如果需要保持默认就是NULL
								      //第三个参数是新线程开始执行的函数,返回一个无类型指针,并且接受一个无类型指针作为参数
								      //第四个参数是主线程传给新建线程的唯一参数,如果不需要参数直接写NULL
	if(ret != 0)
	{
		printf("create pthread error\n");
		exit(EXIT_FAILURE);
	}
	pthread_join(tid1,NULL);	//第一个参数是等待线程的id,第二个参数是用户定义指针,用来存储被等待线程的返回值	
	pthread_join(tid2,NULL);	//第一个参数是等待线程的id,第二个参数是用户定义指针,用来存储被等待线程的返回值	

	pthread_mutex_destroy(&mutex);	//用于销毁互斥锁,参数是指向互斥锁的指针
	pthread_cond_destroy(&cond);
	return 0;
}

编译结果

image-20240321183438697

代码分析:当主控线程创建新线程后,系统可能会给线程1分配时间片,此时线程1调用函数pthread_cond_wait等待条件满足,但此时条件不满足,所以进入到阻塞状态。系统转而去执行线程2,当经过三轮的执行后,满足条件,线程2调用pthread_cond_signal函数唤醒线程1执行,然后线程1重新将g_data的值赋值为0,以此类推的执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

日落星野

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

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

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

打赏作者

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

抵扣说明:

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

余额充值