线程(2):补充的最重要的重点!!!!

1.创建多个线程:

创建多个线程时,一般由主线程统一创建,并等待释放资源或者分离线程,不要递归创建

  1. 多个线程如果任务相同,则可以使用同一个线程执行函数
  2. 多个线程如果任务不同,则可以使用不同的线程执行函数

    示例代码:进程A和进程B执行相同的任务:
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
// 线程执行函数
void* do_thread_function(void* args)
{
	for(int i=0;i<5;i++)	
	{
		printf("i=%d\n",i+1);
		sleep(1);
	}
	pthread_exit(NULL);
}
int main()
{
	pthread_t threadids[2]={0};
	int result;
	for(int i=0;i<2;i++)
	{
	int result =pthread_create(threadids+i,NULL,do_thread_function,NULL);
	if(result!=0)
	{	、
		fprintf(stderr,"pthreaderror:%s\n",strerror(result));
		exit(EXIT_FAILURE);
	}
		printf("threadA id is %ld\n",*(threadids+i));
	}
	pthread_join(threadids[0],NULL);
	pthread_join(threadids[1],NULL);
	return 0;
}

示例代码:进程A和进程B执行不同的任务

cpp 复制代码
#include <unistd.h>
// 线程执行函数
void* do_thread_funA(void* args)
{
	printf("do thread A\n");
	pthread_exit(NULL);
}
void* do_thread_funB(void* args)
{
	printf("do thread b\n");
	pthread_exit(NULL);
}
int main()
{
	pthread_t thread_id_a,thread_id_b;
	int result = pthread_create(&thread_id_a,NULL,do_thread_funA,NULL);
	if(result!=0)
	{
		fprintf(stderr,"pthread error:%s\n",strerror(result));
	exit(EXIT_FAILURE);
	}
	printf("threadA id is %ld\n",thread_id_a);
	pthread_detach(thread_id_a);
	result = pthread_create(&thread_id_b,NULL,do_thread_funB,NULL);
	if(result!=0)
	{
	fprintf(stderr,"pthread error:%s\n",strerror(result));
	exit(EXIT_FAILURE);
	}
	printf("threadB id is %ld\n",thread_id_b);
	pthread_detach(thread_id_b);
	while(1);
	return 0;
}

2.线程的通信

为什么需要线程通信?

线程是操作系统调度的最小单元,有自己的栈空间,可以按照既定的代码逐步的执行,但是如果每个线程间都孤立的运行,那就会造资源浪费。所以在现实中,我们需要这些线程间可以按照指定的规则共同完成一件任务,所以这些线程之间就需要互相协调,这个过程被称为线程的通信。线程通信就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺。
一、主线程向子线程传递参数

通过 pthread_create 函数的第4个参数 arg 进行传递

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// 线程执行函数
void* do_thread_function(void* args)
{
printf("num = %d\n",*(int*)args);
pthread_exit(NULL);
}
int main()
{
pthread_t thread_id;
int num = 100;
int result =
pthread_create(&thread_id,NULL,do_thread_function,&num);
if(result!=0)
{
fprintf(stderr,"pthread error:%s\n",strerror(result));
exit(EXIT_FAILURE);
}
printf("thread id is %ld\n",thread_id);
pthread_join(thread_id,NULL);
return 0;
}

二、子线程给主线程传递参数

子线程给主线程传参的方式如下:

  • 在子线程将需要返回的值存储在 pthread_exit 函数中的 retval 参数中
  • 在主线程中通过 pthread_join 函数的第2个参数 retval 得到返回, pthread_join 函数会将线程的返回值(指针)保存到 retval 中
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// 线程执行函数
void* do_thread_function(void* args)
{
	static float score = 92.3;
	pthread_exit(&score);
}
int main()
{
	pthread_t thread_id;
	int result = pthread_create(&thread_id,NULL,do_thread_function,NULL);
	if(result!=0)
	{
		fprintf(stderr,"pthread error:%s\n",strerror(result));
		exit(EXIT_FAILURE);
	}
	printf("thread id is %ld\n",thread_id);
	void* res = NULL;
	pthread_join(thread_id,&res);
	printf("*res = %.2f\n",*(float*)res);
	return 0;
}

3. 线程间互斥锁


一、关于线程互斥锁

线程的主要优势在于能够通过全局变量来共享信息,不过这种便捷的共享是有代价的:

  • 必须确保多个线程不会同时修改同一变量
  • 某一线程不会读取正由其他线程修改的变量,实际就是 不能让两个线程同时对临界区进行访问
  • 线程互斥锁则可以用于解决多线程资源竞争问题
    示例代码:
    创建两个子线程,定义一个全局变量 global = 0,子线程对此全局变量进行加1操作
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
int global = 0;
//线程执行函数
void* do_thread(void* argv)
{
	//循环对global进行加1操作
	int loops = *(int*)argv;
	for(int i=0;i<loops;i++)
	{
		int temp = global;
	temp++;
	global=temp;
	}
	pthread_exit(NULL);
}
int main(int argc,char* argv[])
{
	if(argc !=2)
	{
		fprintf(stderr,"arguments must be 2:< cmd > <count>\n");
		exit(EXIT_FAILURE);
	}
	// 获取循环次数
	int loopCount = atoi(argv[1]);
	// 循环的方式创建两个线程
	pthread_t tids[2]={0};
	int err;
	for(int i=0;i<2;i++)
	{
		err = pthread_create(tids+i,NULL,do_thread,&loopCount);
		if(err!=0)
		{
			fprintf(stderr,"pthread_create
			failed:%s\n",strerror(err));
			exit(EXIT_FAILURE);
		}
	}
	pthread_join(tids[0],NULL);
	pthread_join(tids[1],NULL);
	// 打印全局变量
	printf("global=%d\n",global);
	return 0;
}

Tips:

将数字字符串转换为整数

cpp 复制代码
int atoi(const char *nptr);

运行结果:

`

问题:当循环次数为100000和10000000时,打印结果出错原因:

解决方案:

使用线程互斥锁

5.线程互斥锁

  1. 线程互斥锁工作机制
    当线程A获得锁,另外一个线程B在获得锁时则会阻塞,直到线程A释放锁,线程B才会获得锁。
  2. 线程互斥锁工作原理
    本质上是一个pthread_mutex_t类型的变量,假设名为 v
    1.当 v = 1,则表示当前临界资源可以竞争访问,得到互斥锁的线程则可以访问,此时 v = 0
    2 当 v = 0,则表示临界资源正在被某个线程访问,其他线程则需要等待
  3. 线程互斥锁的特点
    互斥锁是一个pthread_mutex_t 类型的变量,就代表一个互斥锁
    如果两个线程访问的是同一个 pthread_mutex_t 变量,那么它们访问了同一个互斥锁对应的变量定义在 pthreadtypes.h 头文件中,是一个共用体中包含一个结构体

6.线程互斥锁的初始化

线程互斥锁的初始化方式主要分为两种

  1. 静态初始化

    定义 pthread_mutex_t 类型的变量,然后对其初始化为 PTHREAD_MUTEX_INITIALIZER
    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER

  2. 动态初始化

    动态初始化主要涉及两个函数 pthread_mutex_init 函数与 pthread_mutex_destroy 函数

pthread_mutex_init 函数

cpp 复制代码
1.函数头文件
#include <pthread.h>
2.函数原型
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
3.函数功能
初始化线程互斥锁
4.函数参数
mutex:线程互斥锁对象指针
attr:线程互斥锁属性
5.函数返回值
成功:返回0
失败:返回错误编码

pthread_mutex_destroy 函数

cpp 复制代码
1.函数头文件
#include <pthread.h>
2.函数原型
int pthread_mutex_destroy(pthread_mutex_t *mutex);
3.函数功能
销毁线程互斥锁
4.函数参数
mutex:线程互斥锁指针
5.函数返回值
成功:返回 0
失败:返回错误编码

7.线程互斥锁的操作

线程互斥锁的操作主要分为 获取锁(lock)释放锁(unlock)

获取锁的函数:pthread_mutex_lock

cpp 复制代码
函数头文件
#include <pthread.h>
函数原型
int pthread_mutex_lock(pthread_mutex_t *mutex);
函数功能
将互斥锁进行锁定,如果已经锁定,则阻塞线程
函数参数
mutex:线程互斥锁指针
函数返回值
成功:返回0
失败:返回错误码

释放锁的函数:pthread_mutex_unlock

cpp 复制代码
函数头文件
#include <pthread.h>
函数原型
int pthread_mutex_unlock(pthread_mutex_t *mutex);
函数功能
解除互斥锁锁定状态,解除后,所有线程可以重新竞争锁
函数参数
mutex:线程互斥锁对象的指针
函数返回值
成功:返回 0
失败:返回错误码

8.示例代码:

cpp 复制代码
using namespace std;
#include<iostream>
#include<string>
#include<vector>
#include<deque>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<ptherad.h>
int global = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void * start_routinue(void* arg){
	int loop = *(int*)arg;
	pthread_mutex_lock(&mutex);
	for(int i = 0;i < loop;i++){
		int temp = global;
		temp++;
		global = temp;
	}
	pthread_mutex_unlock(&mutex);
	pthread_exit(NULL);
}
int main(int argc,char* argv[]){
	if(argc < 2){
		cout << "error" << endl;
		exit(EXIT_FAILURE);
	}
	int count = atoi(argv[1]);
	vector<pthread_t> arr;
	arr.push_back(0);
	arr.push_back(0);
	for(vector<pthread_t> :: iterator it = arr.begin();it != arr.end();it++){
		int ret = pthread_create(&(*it),NULL,start_routinue,&count);
		if(ret != 0){
			cout << "create failed" << endl;
			exit(EXIT_FAILURE);
		}
	}
	pthread_join(arr.at(0),NULL);
	pthread_join(arr.at(1),NULL);
	cout << global << endl;
}
相关推荐
哎呦喂-ll21 分钟前
Linux进阶:环境变量
linux
Rverdoser23 分钟前
Linux环境开启MongoDB的安全认证
linux·安全·mongodb
PigeonGuan33 分钟前
【jupyter】linux服务器怎么使用jupyter
linux·ide·jupyter
东华果汁哥1 小时前
【linux 免密登录】快速设置kafka01、kafka02、kafka03 三台机器免密登录
linux·运维·服务器
咖喱鱼蛋1 小时前
Ubuntu安装Electron环境
linux·ubuntu·electron
ac.char2 小时前
在 Ubuntu 系统上安装 npm 环境以及 nvm(Node Version Manager)
linux·ubuntu·npm
肖永威2 小时前
CentOS环境上离线安装python3及相关包
linux·运维·机器学习·centos
tian2kong2 小时前
Centos 7 修改YUM镜像源地址为阿里云镜像地址
linux·阿里云·centos
布鲁格若门2 小时前
CentOS 7 桌面版安装 cuda 12.4
linux·运维·centos·cuda