线程与互斥锁

news2025/1/12 19:24:53

一、线程

1、定义

        进程的创建、销毁与切换存在着较大的时空开销,因此人们急需一种轻型的进程技术来减少开销。在80年代,线程的概念开始出现,线程被设计成进程的一个执行路径,同一个进程中的线程共享进程的资源,因此系统对线程的调度所需的成本远远小于进程。

2、特性

①线程是一个基本的CPU执行单元,也是程序执行流的最小单位。

②每个线程都有一个线程TID、线程控制块(TCB)

③同一进程的不同线程间共享进程的资源

3、实现方式

4、线程与进程的区别

本质区别:进程是操作系统资源分配的基本单位,而线程是CPU任务调度和执行的基本单位。

包含关系:一个进程至少有一个线程,线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

资源开销:每个进程都有独立的地址空间,进程之间的切换会有较大的开销;线程可以看做轻量级的进程,同一个进程内的线程共享进程的地址空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。

影响关系:一个进程崩溃后,在保护模式下其他进程不会被影响,但是一个线程崩溃可能导致整个进程被操作系统杀掉,所以多进程要比多线程健壮。

5、相关函数

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:用于存储新创建的线程的标识符(tid)

        const pthread_attr_t *attr:指向线程属性对象的指针,设置为 NULL 以使用默认属性

        void *(*start_routine) (void *):指向线程函数的指针,这个线程函数是线程开始执行时调用的函数(线程处理函数),线程处理函数的名字:void *pthread_task(void *arg)

        void *arg:传递给线程处理函数的参数,这个参数的值将被传递给 start_routine 函数

函数返回值:

        成功  返回0

        失败  返回-1

注意:当你声明一个 pthread_t 类型的变量时,你实际上是在为该线程分配一个标识符(tid),但这个标识符本身在声明时并不会被自动初始化或分配给一个实际的线程

示例1:利用pthread_create函数,创建一个子线程,观察一下。

现象:

代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//线程处理函数
void *pthread_task(void *arg)
{
	printf("hello_world\r\n");

}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret;
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, NULL);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
    //输出一下tid
	printf("tid:%ld\r\n",tid);
	
	while(1);
	
	return 0;
}

示例2:利用pthread_create函数,将对应的参数(数字),传递给对应的线程处理函数。

现象:

代码:

include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   long a=(long)arg;
   printf("a=%ld\r\n",a);
   
   
   printf("task%ld=hello\r\n",a);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret;
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, (void *)1);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid:%ld\r\n",tid);
	
	while(1);
	
	return 0;
}

示例3:利用pthread_create函数,将对应的参数,传递给对应的线程处理函数。-传递参数是变量

现象:

代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   int a=*(int*)arg;
   printf("a=%d\r\n",a);
   
   
   printf("task%d=hello\r\n",a);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret;
	
	//定义变量
	int a = 10;
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, (void *)&a);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid:%ld\r\n",tid);
	
	while(1);
	
	return 0;
}

示例4:利用pthread_create函数,传递一个结构体,观察一下

现象:

代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//声明一个结构体
struct test{
	int test1;
	char test2;
};

//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   struct test* a=(struct test*)arg;
   
   printf("test1=%d\r\n",a->test1);
   printf("test2=%c\r\n",a->test2);
   
   printf("task%d=hello\r\n",a->test1);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret;
	
	//定义结构体变量
	struct test a={10,'c'};
	
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, (void *)&a);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid:%ld\r\n",tid);
	
	while(1);
	
	return 0;
}

pthread_exit

函数功能:

        退出当前的一个线程

头文件:

        #include <pthread.h>

函数原型:

        void pthread_exit(void *retval);

函数参数:

        void *retval:传递的数据,如果线程不需要返回任何特定的值,可以传递 NULL。

函数返回值:无

示例:调用一下退出函数,pthread_exit函数,观察一下对应的现象

现象:

代码:


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//声明一个结构体
struct test{
	int test1;
	char test2;
};

//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   struct test* a=(struct test*)arg;
   
   printf("test1=%d\r\n",a->test1);
   printf("test2=%c\r\n",a->test2);

   //退出当前线程
   pthread_exit(NULL);

   
   printf("task%d=hello\r\n",a->test1);
   
   
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret;
	
	//定义结构体变量
	struct test a={10,'c'};
	
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, (void *)&a);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid:%ld\r\n",tid);
	
	while(1);
	
	return 0;
}

pthread_join

函数功能:

        等待对应的子线程运行结束,并且获取子线程传递过来的参数

头文件:

        #include <pthread.h>

函数原型:

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

函数参数:

        pthread_t thread:对应创建子线程的tid号

        void **retval:对应的子线程退出的时候,传递的参数可以接收一下

函数返回值:

        成功 返回0

        失败 返回-1

示例:利用pthread_join函数,可以等待对应的子线程运行结束,观察对应的现象

现象:

代码: 


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//声明一个结构体
struct test{
	int test1;
	char test2;
};

//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   struct test* a=(struct test*)arg;
   
   printf("test1=%d\r\n",a->test1);
   printf("test2=%c\r\n",a->test2);
   
   printf("task%d=hello\r\n",a->test1);
   
   //退出当前线程
   pthread_exit(NULL);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret;
	
	//定义结构体变量
	struct test a={10,'c'};
	
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, (void *)&a);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid:%ld\r\n",tid);
	
	//等待线程结束
	ret = pthread_join(tid,NULL);
	if(ret == 0)
	printf("exit success\r\n");
	
	return 0;
}

pthread_cancel

函数功能:

        取消对应线程的执行

头文件:

        #include <pthread.h>

函数原型:

        int pthread_cancel(pthread_t pthread);

函数参数:

        pthread_t thread:对应创建子线程的tid号

函数返回值:

        成功 返回0

        失败 返回-1

补充:该函数执行后,目标线程不一定立即执行,需要满足条件—一般情况下,子线程执行完毕

示例:利用pthread_cancel函数,取消对应的子线程运行,观察对应的现象

现象:

代码: 


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//声明一个结构体
struct test{
	int test1;
	char test2;
};

//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   struct test* a=(struct test*)arg;
   
   printf("test1=%d\r\n",a->test1);
   printf("test2=%c\r\n",a->test2);
   
   printf("task%d=hello\r\n",a->test1);
   
   //退出当前线程
   pthread_exit(NULL);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret;
	
	//定义结构体变量
	struct test a={10,'c'};
	
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, (void *)&a);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid:%ld\r\n",tid);
	
	//取消对应的子线程
	ret = pthread_cancel(tid);
	printf("ret:%d\r\n",ret);
	
	//等待线程结束
	ret = pthread_join(tid,NULL);
	if(ret == 0)
	printf("exit success\r\n");
	
	return 0;
}

综合实例

示例1:利用两个子线程实现交替报数---子线程1—1   子线程2---2

现象:

代码: 


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//线程1处理函数
void *pthread_task1(void *arg)
{
   //输出对应的arg   long --类型
   int a=*(int*)arg;
   sleep(1);
   
   int i = 1;
   while(1)
   {
	   printf("task%d=%d\r\n",a,i);
	   i += 2;
	   sleep(2);
   }
   
   //退出当前线程
   pthread_exit(NULL);
}

//线程2处理函数
void *pthread_task2(void *arg)
{
   //输出对应的arg   long --类型
   int a=*(int*)arg;
 	sleep(1);
 	
   int i = 2;
   while(1)
   {
   	sleep(1);
	   printf("task%d=%d\r\n",a,i);
	   i += 2;
	   sleep(1);
   }
   
   //退出当前线程
   pthread_exit(NULL);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid1,tid2;
	int ret;
	
	//定义结构体变量
	int a = 1;
	int b = 2;

	//创建线程1
	ret = pthread_create(&tid1,NULL, pthread_task1, (void *)&a);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid1:%ld\r\n",tid1);
	
	
	//创建线程2
	ret = pthread_create(&tid2,NULL, pthread_task2, (void *)&b);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid2:%ld\r\n",tid2);

	
	//等待线程结束
	ret = pthread_join(tid1,NULL);
	if(ret == 0)
	printf("exit success\r\n");
	
	ret = pthread_join(tid2,NULL);
	if(ret == 0)
	printf("exit success\r\n");
	
	return 0;
}

示例2:可以利用pthread_exit()传递参数,利用pthread_join()函数可以接收一下

现象:

代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//声明一个结构体
struct test{
	int test1;
	char test2;
};

//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   struct test* a=(struct test*)arg;
   
   printf("test1=%d\r\n",a->test1);
   printf("test2=%c\r\n",a->test2);
   
   printf("task%d=hello\r\n",a->test1);
   
   //退出当前线程
   pthread_exit(86);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid;
	int ret,result;
	
	//定义结构体变量
	struct test a={10,'c'};
	
	//创建线程
	ret = pthread_create(&tid,NULL, pthread_task, (void *)&a);
	if(ret == 0)
	{
		printf("pthread create success\r\n");
	}
	printf("tid:%ld\r\n",tid);
	
	//等待线程结束
	ret = pthread_join(tid,(void *)&result);//result 已经是 void* 类型(通过转换)
	if(ret == 0)
	printf("exit success\r\n");
	//观察退出线程返回的值
	printf("result:%d\r\n",result);
	
	return 0;
}

示例3:利用两个线程实现自由报数

现象:

 代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
int i = 1;
//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   int a=*(int*)arg;
   
  
   while(1)
   {
	   printf("task%d=%d\r\n",a,i);
	   i++;
	   sleep(1);
   }
   
   //退出当前线程
   pthread_exit(NULL);
}


int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid1,tid2;
	int ret;
	
	//定义结构体变量
	int a = 1;
	int b = 2;

	//创建线程1
	ret = pthread_create(&tid1,NULL, pthread_task, (void *)&a);
	
	usleep(1000);
	//创建线程2
	ret = pthread_create(&tid2,NULL, pthread_task, (void *)&b);

	
	//等待线程结束
	ret = pthread_join(tid1,NULL);
	ret = pthread_join(tid2,NULL);
	
	
	return 0;
}

示例4:利用三个线程实现交替报数

现象:

代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//线程1处理函数
void *pthread_task1(void *arg)
{
   //输出对应的arg   long --类型
   int a=*(int*)arg;
   
   int i = 1;
   while(1)
   {
	   printf("task%d=%d\r\n",a,i);
	   i += 3;
	   sleep(3);
   }
   
   //退出当前线程
   pthread_exit(NULL);
}

//线程2处理函数
void *pthread_task2(void *arg)
{
   //输出对应的arg   long --类型
   int b=*(int*)arg;
   
   int i = 2;
   while(1)
   {
	   sleep(1);
	   printf("task%d=%d\r\n",b,i);
	   i += 3;
	   sleep(2);
   }
   
   //退出当前线程
   pthread_exit(NULL);
}

//线程3处理函数
void *pthread_task3(void *arg)
{
   //输出对应的arg   long --类型
   int c=*(int*)arg;
   
   int i = 3;
   while(1)
   {
	   sleep(2);
	   printf("task%d=%d\r\n",c,i);
	   i += 3;
	   sleep(1);
   }
   
   //退出当前线程
   pthread_exit(NULL);
}

int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid1,tid2,tid3;
	int ret;
	
	//定义结构体变量
	int a = 1;
	int b = 2;
	int c = 3;

	//创建线程1
	ret = pthread_create(&tid1,NULL, pthread_task1, (void *)&a);
	//创建线程2
	ret = pthread_create(&tid2,NULL, pthread_task2, (void *)&b);
	//创建线程3
	ret = pthread_create(&tid3,NULL, pthread_task3, (void *)&c);
	
	
	//等待线程结束
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);
	
	return 0;
}

示例5:利用线程实现冒泡排序的功能

思路:

./a.out          11        6               8            17          2

argv[0]   argv[1]   argv[2]    argv[3]  argv[4]  argv[5]

现象:

 代码:

代码:
法1:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   int a=*(int*)arg;
   
   usleep(a*500);
   
   printf(" %d",a);

   //退出当前线程
   pthread_exit(NULL);
}


int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid[argc-1];
	int ret;
	
	for(int i = 0;i < argc-1;i++)
	{
		//将字符转为整数
		ret = atoi(argv[i+1]);
		printf("ret%d:%d\r\n",i+1,ret);
		//创建线程
		pthread_create(&tid[i],NULL, pthread_task, (void *)&ret);
		

	
	}
	
	//等待线程结束
	for(int i = 0;i < argc-1;i++)
	{
		pthread_join(tid[i],NULL);
	}

	printf("\n");
	
	return 0;
}

//法2
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
int buff[20]={0};
int i = 0;

//线程处理函数
void *pthread_task(void *arg)
{
   //输出对应的arg   long --类型
   int a=buff[i++];
   
   usleep(a*1000);
   
   printf(" %d",a);

   //退出当前线程
   pthread_exit(NULL);
}


int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid[argc-1];
	int ret;
	
	for(int i = 0;i < argc-1;i++)
	{
		//将字符转为整数
		ret = atoi(argv[i+1]);
		printf("ret%d:%d\r\n",i+1,ret);
		
		//存储到数组中
		buff[i]=ret;
	}
	
	for(int i = 0;i < argc-1;i++)
	{
		//创建线程
		pthread_create(&tid[i],NULL, pthread_task, NULL);
	}
	//等待线程结束
	for(int i = 0;i < argc-1;i++)
	{
		pthread_join(tid[i],NULL);
	}

	printf("\n");
	
	return 0;
}

二、互斥锁

1、定义

        在线程(pthread)库中,互斥锁(mutex)是用于线程同步的一种机制,确保在同一时刻只有一个线程可以访问共享资源Mutex是一种简单加锁函数控制对共享资源的存取,这个互斥锁只有两种状态(上锁和解锁),锁可以看出某种意义上的全局变量。

2、互斥锁操作流程

使用互斥锁步骤:

一个互斥锁,对应一个共享资源。

标注:主线程主要完成锁的初始化以及销毁锁   子线程里面涉及加锁和解锁的过程。

主线程:

第一步:创建并初始化互斥锁。

第二步:创建多个子线程。

第三步:等待子线程运行结束。

第四步:销毁互斥锁。

第五步:结束进程

子线程:

第一步:在操作共享资源之前上锁。

第二步:操作共享资源。

第三步:在操作共享资源以后解锁。

第四步:子线程运行结束

3、相关函数

pthread_mutex_init

函数功能:

        运行时动态创建一个线程互斥锁

头文件:

        #include <pthread.h>

函数原型:

        int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

函数参数:

        thread_mutex_t *mutex:指向要初始化的互斥锁的指针。

       const pthread_mutexattr_t *attr:用于指定互斥锁属性的对象指针,通常可以传递 NULL 以使用默认属性。

函数返回值:

        成功 返回0

        失败 返回-1

pthread_mutex_lock

函数功能:

        锁定互斥锁

头文件:

        #include <pthread.h>

函数原型:

        int pthread_mutex_lock(pthread_mutex_t *mutex);

函数参数:

        pthread_mutex_t *mutex: 指向要锁定的互斥锁的指针

函数返回值:

        成功 返回0

        失败 返回-1

注意:不论哪种类型的锁,都不能被两个不同的线程同时得到,必须等到解锁。

pthread_mutex_unlock

函数功能:

        释放互斥锁(解锁)

头文件:

        #include <pthread.h>

函数原型:

        int pthread_mutex_unlock(pthread_mutex_t *mutex);

函数参数:

        pthread_mutex_t *mutex: 指向要释放的互斥锁的指针

函数返回值:

        成功 返回0

        失败 返回-1

注意:释放锁之前必须确保当前线程已经锁定了该锁

pthread_mutex_destroy

函数功能:

        销毁一个互斥锁,释放已经占有的共享资源

头文件:

        #include <pthread.h>

函数原型:

        int pthread_mutex_destroy(pthread_mutex_t *mutex);

函数参数:

        pthread_mutex_t *mutex: 指向要销毁的互斥锁的指针

函数返回值:

        成功 返回0

        失败 返回-1

示例:基于互斥锁的所有API函数,观察上锁、解锁、摧毁锁的整个过程

现象:

代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//定义互斥锁
pthread_mutex_t lock;

//线程1处理函数
void *pthread_task1(void *arg)
{
   //加锁
  int ret = pthread_mutex_lock(&lock);
  if(ret == 0)
  printf("task1,success\r\n");
   
  printf("hello\r\n");
  
  //解锁
  ret = pthread_mutex_unlock(&lock);
  if(ret == 0)
  printf("unlock success\r\n");
  
  //退出当前线程
  pthread_exit(NULL);
}
//线程2处理函数
void *pthread_task2(void *arg)
{
   //加锁
  int ret = pthread_mutex_lock(&lock);
  if(ret == 0)
  printf("task2,success\r\n");
   
  printf("world\r\n");
  
  //解锁
  ret = pthread_mutex_unlock(&lock);
  if(ret == 0)
  printf("unlock success\r\n");
  
  //退出当前线程
  pthread_exit(NULL);
}

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

	//初始化互斥锁
	ret = pthread_mutex_init(&lock, NULL);
	if(ret == 0)
	printf("lock init success\r\n");

	//创建子线程
	pthread_create(&tid1,NULL, pthread_task1, NULL);
	pthread_create(&tid2,NULL, pthread_task2, NULL);
	
	//等待线程结束
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	
	//摧毁互斥锁
	ret = pthread_mutex_destroy(&lock);
	if(ret == 0)
    printf("destroy lock success\r\n");

	return 0;
}

综合实例

综合1:利用线程互斥锁实现交替报数的功能

现象:

代码:

法1:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//定义互斥锁
pthread_mutex_t lock;
int i = 1;

//线程处理函数
void *pthread_task(void *arg)
{
	int n = *(int *)arg;	
	
   //上锁
	pthread_mutex_lock(&lock);
	

	printf("task%d=%d\r\n",n,i);
	i += 1;
	
	//解锁
   pthread_mutex_unlock(&lock);
   
   
   //退出当前线程
   pthread_exit(NULL);
}


int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid1,tid2,tid3;
	int ret;
	
	//初始化互斥锁
	ret = pthread_mutex_init(&lock, NULL);
	
	int a = 1;
	int b = 2;
	int c = 3;

	while(1)
	{
		//创建线程1
		pthread_create(&tid1,NULL, pthread_task,(void *)&a);
		sleep(1);
		//创建线程2
		pthread_create(&tid2,NULL, pthread_task,(void *)&b);
		sleep(1);
		//创建线程3
		pthread_create(&tid3,NULL, pthread_task,(void *)&c);
		sleep(1);
	}
	
	//等待线程结束
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);
	
	//摧毁互斥锁
	ret = pthread_mutex_destroy(&lock);
	if(ret == 0)
   printf("destroy lock success\r\n");
	
	return 0;
}

法2:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//定义互斥锁
pthread_mutex_t lock;
int i = 1;

//线程1处理函数
void *pthread_task1(void *arg)
{
	int n = *(int *)arg;	
	
	while(1)
	{
		
		if(i%3==1)
		{
			//上锁
		   pthread_mutex_lock(&lock);
			printf("task%d=%d\r\n",n,i);
			i += 1;
			//解锁
			pthread_mutex_unlock(&lock);
		}
		else
			sleep(1);//切换线程
	}
   
   //退出当前线程
   pthread_exit(NULL);
}
//线程2处理函数
void *pthread_task2(void *arg)
{
	int n = *(int *)arg;
	
	while(1)
	{
		
		if(i%3==2)
		{
			//上锁
			pthread_mutex_lock(&lock);
			printf("task%d=%d\r\n",n,i);
			i += 1;
			//解锁
			pthread_mutex_unlock(&lock);
		}
		else
			sleep(1);//切换线程
	}
   
   
   //退出当前线程
   pthread_exit(NULL);
}
//线程3处理函数
void *pthread_task3(void *arg)
{
	int n = *(int *)arg;	
	
    while(1)
	{
		
		if(i%3==0)
		{
			//上锁
			pthread_mutex_lock(&lock);
			printf("task%d=%d\r\n",n,i);
			i += 1;
			//解锁
			pthread_mutex_unlock(&lock);
		}
		else
			sleep(1);//切换线程
	}
   
   
   //退出当前线程
   pthread_exit(NULL);
}


int main(int argc,char* argv[])
{
	//生成线程的标识符
	pthread_t tid1,tid2,tid3;
	int ret;
	
	//初始化互斥锁
	ret = pthread_mutex_init(&lock, NULL);
	
	int a = 1;
	int b = 2;
	int c = 3;

	//创建线程1
	pthread_create(&tid1,NULL, pthread_task1,(void *)&a);
	//创建线程2
	pthread_create(&tid2,NULL, pthread_task2,(void *)&b);
	//创建线程3
	pthread_create(&tid3,NULL, pthread_task3,(void *)&c);
	
	
	//等待线程结束
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);
	
	//摧毁互斥锁
	ret = pthread_mutex_destroy(&lock);
	if(ret == 0)
   printf("destroy lock success\r\n");
	
	return 0;
}

法3:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//定义一个锁
pthread_mutex_t lock;
int i=1;

//线程的处理函数
void *pthread_task(void *arg)
{
   long a=(long)arg;
  
  while(1)
  {
   //加锁操作
   pthread_mutex_lock(&lock);
   printf("task%ld=%d\r\n",a,i);
   i++;
   
   //解锁函数
   pthread_mutex_unlock(&lock);
   sleep(1);
  }
   
   //退出线程
   pthread_exit(NULL);

}


int main(int argc,char *argv[])
{
     int ret;
	 pthread_t tid1,tid2,tid3;
     //初始化线程互斥锁
	 ret=pthread_mutex_init(&lock,NULL);
	 if(ret == 0)
	 {
	    printf("pthread_mutex_init success\r\n");
	 }
	 
	 //创建子线程
	 pthread_create(&tid1, NULL,pthread_task,(void *)1);
	 //创建子线程
	 pthread_create(&tid2, NULL,pthread_task,(void *)2);
	 //创建子线程
	 pthread_create(&tid3, NULL,pthread_task,(void *)3);
	 
	 //等待对应的子线程运行结束
	 pthread_join(tid1,NULL);
	 pthread_join(tid2,NULL);
	 pthread_join(tid3,NULL);
     
	 //销毁对应的互斥锁
     pthread_mutex_destroy(&lock);
     return 0;
}

 

综合2:售票系统---模拟一个火车站的售票系统

分析思路:

1:对应的主函数里面(main---创建两个子线程(售票窗口—子线程1  退  票窗口—子线程2))

2:主线程里面可以等待对应子线程1和子线程2运行结束

3:子线程1(售票窗口)---提示一下当前是售票窗口---判断使用者是否购  票---如果购票,再进行处理。如果不购票,切换到(退票窗口)

4: 子线程2(退票窗口)---提示一下当前是退票窗口—判断使用者是否退票  —如果退票,再进行处理。如果不退票,切换到(售票窗口)

现象:

 

代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>
//定义互斥锁
pthread_mutex_t lock;
//票数
int check = 100;

//线程1处理函数---售票窗口
void *pthread_task1(void *arg)
{
	
		
	char n;
		
	
	while(1)
	{
		//上锁
		pthread_mutex_lock(&lock);
		//提示当前是售票窗口
		printf("******************************************\r\n");
		printf("------------------售票窗口----------------\r\n");
		printf("******************************************\r\n");
	
		//输入
		printf("是否购票?请输入'Y'or'N\'\r\n");
		scanf("%c",&n);
		getchar();
		//判断是否购票
		if(n=='Y' || n=='y')
		{
			check--;
			if(check == 0)
			{
				printf("车票售罄\r\n");
				//解锁
				pthread_mutex_unlock(&lock);//退出线程前需要解锁
				break;
			}
			printf("购票成功,票数余量:%d\r\n",check);
			
			//解锁
			pthread_mutex_unlock(&lock);
			sleep(1);
		}
		else
		{
			//解锁
			pthread_mutex_unlock(&lock);
			sleep(1);
		}
		
	}
	
    //退出当前线程
    pthread_exit(NULL);
}
//线程2处理函数---退票窗口
void *pthread_task2(void *arg)
{
	char n;
	while(1)
	{
		//上锁
		pthread_mutex_lock(&lock);
		//提示当前是退票窗口
		printf("******************************************\r\n");
		printf("------------------退票窗口----------------\r\n");
		printf("******************************************\r\n");
	
		//输入
		printf("是否退票?请输入'Y'or'N\'\r\n");
		scanf("%c",&n);
		getchar();
		
		if(n=='Y' || n=='y')
		{
			if(check == 100)
			{
				printf("车票已满\r\n");
				//解锁
				pthread_mutex_unlock(&lock);//退出线程前需要解锁
				break;
			}
			printf("退票成功,票数余量:%d\r\n",++check);
			
			//解锁
			pthread_mutex_unlock(&lock);
			sleep(1);
		}
		else
		{
			//解锁
			pthread_mutex_unlock(&lock);
			sleep(1);
		}
	}
   //退出当前线程
   pthread_exit(NULL);
}

int main(int argc,char* argv[])
{
	char val;
	
	//生成线程的标识符
	pthread_t tid1,tid2;
	
	//初始化互斥锁
	pthread_mutex_init(&lock, NULL);
	
	//创建线程1
	pthread_create(&tid1,NULL, pthread_task1, NULL);
	//创建线程2
	pthread_create(&tid2,NULL, pthread_task2, NULL);

	
	//等待线程结束
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	
	//摧毁互斥锁
	pthread_mutex_destroy(&lock);
	
	return 0;
}

说明:

CPU并发高速执行,几乎是同时的。

使用sleep函数阻塞(切换线程->CPU执行其它线程,直到sleep时间到,回来再继续执行当前线程),需要考虑sleep后是否有其它阻塞(scanf、fgetc\fgets....);特别是在多线程操作的时候,当前线程退出后,其它存在的线程再也无法切换到当前线程了

如下图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何搭建 Vue.js 开源项目的 CI/CD 流水线

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

uni app 写的 小游戏,文字拼图?文字拼写?不知道叫啥

从下方的偏旁部首中选在1--3个组成上面文章中的文字&#xff0c;完成的文字标红 不喜勿喷 《满江红》 其中用到了两个文件 strdata.json parameters.json 这两个文件太大 放到资源中了 资源文件 <template><view class"wenzi_page_main"><view c…

DBeaver执行本地的sql语句文件避免直接在客户端运行卡顿

直接在客户端运行 SQL 语句和通过加载本地文件执行 SQL 语句可能会出现不同的性能表现&#xff0c;原因可能包括以下几点&#xff1a; 客户端资源使用&#xff1a; 当你在客户端界面直接输入和执行 SQL 语句时&#xff0c;客户端可能会消耗资源来维护用户界面、语法高亮、自动完…

基于STM32的智能电表可视化设计:ESP8266、AT指令集、python后端Flask(代码示例)

一、项目概述 随着智能家居的普及&#xff0c;智能电表作为家庭用电管理的重要工具&#xff0c;能够实时监测电流、电压及功率&#xff0c;并将数据传输至后台进行分析和可视化。本项目以STM32C8T6为核心&#xff0c;结合交流电压电流监测模块、ESP8266 Wi-Fi模块、OLED显示屏…

MySQL 如何赶上 PostgreSQL 的势头?

原文地址 我与 MySQL 社区的前辈交谈时&#xff0c;经常遇到这个问题&#xff1a;「为什么 MySQL 这么棒&#xff0c;而且&#xff08;至少根据 DB-Engines 的计算&#xff09;仍然比 PostgreSQL 更流行&#xff1b;但它的地位在下降&#xff0c;PostgreSQL 却势不可挡地越来越…

关于在windows系统中编译ffmpeg并导入到自己项目中这件事

关于在windows系统中编译ffmpeg并导入到自己项目中这件事 前因&#xff08;可跳过不看&#xff09; 前阵子由于秋招需求&#xff0c;写了一个简易的安卓播放器&#xff0c;最终因为时间问题还有一些功能没有实现着实可惜&#xff0c;如&#xff1a;倍速播放&#xff0c;快进操…

word中电流符号i或者j,这两个字母的头上的点会消失---完美解决办法

上图中&#xff0c;是我已经改好的格式。 具体解决办法是将公式转成LATEX格式&#xff0c;然后在字母i或者j前加上//&#xff0c;再转换会unicode&#xff0c;专业形式即可解决。更快的解决办法就是复制我在word文档里面写的。 word文档放在资源里面了&#xff0c;有需要自取即…

【C++】std::string和size()函数进阶解析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;基础知识&#xff1a;C 中的std::string字符串的基础概念size() 函数基础使用size()实例&#xff1a;计算字符串长度 &#x1f4af;基于size()的字符串解析和访问方式代码实…

《OpenCV计算机视觉实战项目》——银行卡号识别

文章目录 项目任务及要求项目实现思路项目实现及代码导入模块设置参数对模版图像中数字的定位处理银行卡的图像处理读取输入图像&#xff0c;预处理找到数字边框使用模版匹配&#xff0c;计算匹配得分 画出并打印结果 项目任务及要求 任务书&#xff1a; 要为某家银行设计一套…

【开发环境搭建篇】Visual Studio 2022 安装和使用

本文收录于 《C编程入门》专栏&#xff0c;从零基础开始&#xff0c;介绍C编程入门相关的内容&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、下载三、安装四、使用五、总结 一、前言 本文介绍如何在Windows环境下安装Visual Studio 2022。 什么是Vi…

【面试题】技术场景 5、日志采集ELK

日志采集的重要性与采集方式 重要性&#xff1a;在项目开发、测试及生产环境中&#xff0c;日志是定位系统问题的关键手段&#xff0c;对系统维护与问题排查至关重要。采集方式 常规采集&#xff1a;按天保存日志文件至专门目录&#xff0c;文件名包含项目名、端口及日期&…

matlab的绘图的标题中(title)添加标量以及格式化输出

有时候我们需要在matlab绘制的图像的标题中添加一些变量&#xff0c;这样在修改某些参数后&#xff0c;标题会跟着一块儿变。可以采用如下的方法&#xff1a; x -10:0.1:10; %x轴的范围 mu 0; %均值 sigma 1; %标准差 y normpdf(x,mu,sigma); %使用normpdf函数生成高斯函数…

element plus 使用 upload 组件达到上传数量限制时隐藏上传按钮

最近在重构项目&#xff0c;使用了 element plus UI框架&#xff0c;有个功能是实现图片上传&#xff0c;且限制只能上传一张图片&#xff0c;结果&#xff0c;发现&#xff0c;可以限制只上传一张图片&#xff0c;但是上传按钮还在&#xff0c;如图&#xff1a; 解决办法&…

简单说一下 类

类的定义 类是用来对一个实体&#xff08;对象&#xff09;进行描述&#xff0c;类就是用来描述这个对象具有一些什么属性。 类的定义格式 //创建类 class ClassName{ field; //简单概述为字段(属性)或者成员变量 method; //简单概述为行为或者是成员方法 } cl…

HOW - Form 表单 label 和 wrapper 对齐场景

一、背景 在日常使用 表单 时&#xff0c;我们一般有如下布局&#xff1a; 可以通过 Form 表单提供的配置直接设置&#xff1a; <Formform{form}labelCol{{ span: 4 }}wrapperCol{{ span: 20 }}onFinish{handleSubmit}><Form.Itemlabel"输入框"name"…

01 Oracle自学环境搭建(Windows系统)

1 Oracle12C安装 1.1 下载 官网地址&#xff1a;https://www.oracle.com/ 进入官网→Resource→Customer Downloads 如果没有登录&#xff0c;会提示登录后后才能下载 选择适合自己的版本&#xff08;我电脑是Windows系统 64位&#xff09; 选择需要的安装包进行下载 双击下载…

vue el-table 数据变化后,高度渲染问题

场景&#xff1a;el-table设置了height属性&#xff0c;但是切换查询条件后再次点击查询重新获取data时&#xff0c;el-table渲染的高度会有问题&#xff0c;滚动区域变矮了。 解决办法&#xff1a;使用doLayout方法‌&#xff0c;在表格数据渲染后调用doLayout方法可以重新布局…

vue3+ts+element-plus 输入框el-input设置背景颜色

普通情况&#xff1a; 组件内容&#xff1a; <el-input v-model"applyBasicInfo.outerApplyId"/> 样式设置&#xff1a; ::v-deep .el-input__wrapper {background-color: pink; }// 也可以这样设置 ::v-deep(.el-input__wrapper) {background-color: pink…

【漫话机器学习系列】044.热点对特性的影响(Effect Of One Hot On Feature Importance)

热点对特性的重要性影响&#xff08;Effect of One-Hot Encoding on Feature Importance&#xff09; 一热编码&#xff08;One-Hot Encoding&#xff09; 是处理类别型数据的常用方法&#xff0c;将每个类别特征转换为一组独立的二进制特征。这种方法在提高模型处理非数值数据…

使用MATLAB正则表达式从文本文件中提取数据

使用MATLAB正则表达式从文本文件中提取数据 使用Python正则表达式从文本文件中提取数据的代码请看这篇文章使用正则表达式读取文本数据【Python】-CSDN博客 文本数据格式 需要提取 V 后面的数据, 并绘制出曲线. index 1V 0.000000W 0.000000E_theta 0.000000UINV 0.0…