lesson5 线程的高级属性

本文介绍了Linux线程的高级特性,包括一次性初始化、线程属性设置、互斥量和读写锁的属性、线程私有数据以及线程与fork的交互。一次性初始化确保互斥量只初始化一次,线程属性可以改变栈大小和分离状态。互斥量的进程共享属性允许跨进程同步,而线程私有数据提供线程间的隔离。在使用fork时,需要注意线程安全问题,利用pthread_atfork进行处理。

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

1. 一次性初始化:

互斥量智能初始化一次,再次初始化会出现错误。比如自己写库函数时将互斥量封装进去,这时一次性初始化就用上场了。

定义变量pthread_once_t once_control = PTHREAD_ONCE_INIT;

void init_routine(){

//初始化互斥量

//初始化读写锁

}

接下来在函数pthread_once引用上述变量和函数,多线程只会执行函数指针一次。 

2. 线程属性

创建线程时可以使用pthread_attr_t类型参数变更属性。

如:

a. detachstate线程的分离状态

b. guardsize线程栈末尾的警戒区域大小//预防栈溢出

c. stackaddr线程栈的最低地址

d. stacksize线程栈的大小//不能小于最小值

并不是全部线程都支持这些熟悉,修改前需要先检查。


pthread_attr_setstack //修改栈地址、大小

pthread_attr_getstack //获取栈属性


pthread_attr_setstacksize //设置大小,不能设地址

pthread_attr_sgetstacksize


3. 线程的同步属性

1). 互斥量的属性:

a.进程共享属性:PTHREAD_PROCESS_PRIVATE,同一进程的多个线程访问权限;PTHREAD_PROCESS_SHARED:多个进程访问权限。

如果互斥量在多进程的共享内存区域,那么具有这个属性的互斥量可以同步多进程。

pthread_mutexattr_getpshared/pthread_mutexattr_setshared设置、获取互斥量进程共享属性。

b.类型属性:设置互斥量的类型属性,PTHREAD_MUTEX_NORMAL等;

pthread_mutexattr_settype/pthread_mutexattr_gettype设置、查看出现

2).读写锁属性

3)条件变量属性

4. 示例,互斥量属性操作

#include "apue.h"

int main()
{
	char *shm = "myshm";
	char *shm1 = "myshm1";
	int shm_id, shm_id1;
	char *buf;
	pid_t pid;

	pthread_mutex_t *mutex;
	pthread_mutexattr_t mutexattr;


	//打开共享内存
	shm_id1 = shm_open(shm1, O_RDWR|O_CREAT, 0644);
	//调整共享内存大小
	ftruncate(shm_id1, 100);
	//映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程
	mutex =(pthread_mutex_t *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id1, 0);

	pthread_mutexattr_init(&mutexattr);
#ifdef _POSIX_THREAD_PROCESS_SHARED
	pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
#endif
	pthread_mutex_init(mutex, &mutexattr);

	//打开共享内存
	shm_id = shm_open(shm, O_RDWR|O_CREAT, 0644);
	//调整共享内存大小
	ftruncate(shm_id, 100);
	//映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程
	buf =(char *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id, 0);
	
	pid = fork();
	if(pid==0)
	{	
		//休眠1s,让父进程先运行
		sleep(1);
		printf("I'm child proccess\n");
		
		pthread_mutex_lock(mutex);
		//将共享内存内存修改为hello
		memcpy(buf, "hello", 6);
		printf("child buf is : %s\n", buf);
		pthread_mutex_unlock(mutex);
	}
	else if(pid>0)
	{
		printf("I'm parent proccess\n");

		pthread_mutex_lock(mutex);
		//修改共享内存到内容,改为world
		memcpy(buf, "world", 6);
		sleep(3);
		printf("parent buf is : %s\n", buf);
		pthread_mutex_unlock(mutex);
	}

	pthread_mutexattr_destroy(&mutexattr);
	pthread_mutex_destroy(mutex);
	//解除映射
	munmap(buf, 100);
	//消除共享内存
	shm_unlink(shm);
	//解除映射
	munmap(mutex, 100);
	//消除共享内存
	shm_unlink(shm1);
}


5. 线程私有数据

多个函数多个进程可以访问这个变量,看起来像全局变量;但是它又不是全局变量,线程对这个变量的访问不会彼此产生影响。比如参数errno。

使用私有数据前,要先创建一个与私有数据相关的key,类型pthread_key_t.

使用函数pthread_key_create创建键,创建的键放在参数key指向的内存单元,另一个参数destructor是析构函数。

pthread_key_delete销毁键,当键销毁后与它关联的数据并没有被销毁!

pthread_setspecific将私有数据与key关联

pthread_getspecific获取私有数据的地址

6. 示例,使用私有数据

/*DATE:			2015-4-17
 *AUTHOR:		WJ
 *DESCRIPTION:	线程到私有数据,	一个像errno一样到数据
 */
#include "apue.h"

pthread_key_t key;

void *thread_fun1(void *arg)
{
	printf("thread 1 start!\n");
	int a = 1;
	//将a和key关联
	pthread_setspecific(key, (void *)a);
	sleep(2);
	printf("thread 1 key->data is %d\n", pthread_getspecific(key));
}
void *thread_fun2(void *arg)
{
	sleep(1);
	printf("thread 2 start!\n");
	int a = 2;
	//将a和key关联
	pthread_setspecific(key, (void *)a);
	printf("thread 2 key->data is %d\n", pthread_getspecific(key));
}

int main()
{
	pthread_t tid1, tid2;
	
	//创造一个key
	pthread_key_create(&key, NULL);

	//创造新线程
	if(pthread_create(&tid1, NULL, thread_fun1, NULL))
	{
		printf("create new thread 1 failed\n");
		return;
	}
	if(pthread_create(&tid2, NULL, thread_fun2, NULL))
	{
		printf("create new thread 2 failed\n");
		return;
	}

	//等待新线程结束
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);

	pthread_key_delete(key);

	return;
}

7. 线程与fork

当线程fork时,为子进程创建了整个进程地址空间的副本,子进程通过继承整个地址空间的副本,也会将父进程的互斥量、读写锁、条件变量的状态继承过来。如果父进程中互斥量是锁着的,那么子进程中互斥量也是锁着的,虽然子进程自己还没lock。这时,子进程无法解锁。


子进程内部只有一个线程,由父进程中调用fork函数的线程副本构成。如果调用fork的线程将互斥量锁住,那么子进程会拷贝一个pthread_mutex_lock副本。这时子进程就有机会解锁。


pthread_atfork函数

参数prepare在fork调用前会被调用

参数parent在fork返回父进程前调用

参数child在fork返回子进程前调用

8. 示例,线程与fork安全

/*DATE:			2015-4-17
 *AUTHOR:		DDDDD
 *DESCRIPTION:	安全的使用fork
 *	prepare  在调用fork之前会被调用
 *	parent   在fork返回父进程之前被调用
 *	child	 在fork返回子进程之前被调用
 */
#include "apue.h"

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void prepare()
{
	pthread_mutex_lock(&mutex);
	printf("I'm prepare\n");
}


void parent()
{
	pthread_mutex_unlock(&mutex);
	printf("I'm parent\n");
}
void child()
{
	pthread_mutex_unlock(&mutex);
	printf("I'm child\n");
}

void *thread_fun(void *arg)
{
	sleep(1);
	pid_t pid;
	pthread_atfork(prepare, parent, child);
	pid = fork();
	if(pid==0)
	{
		pthread_mutex_lock(&mutex);
		printf("child process\n");
		pthread_mutex_unlock(&mutex);
	}
	if(pid>0)
	{
		pthread_mutex_lock(&mutex);
		printf("parent process\n");
		pthread_mutex_unlock(&mutex);
	}
}

int main()
{
	pthread_t tid;
	
	
	if(pthread_create(&tid, NULL, thread_fun, NULL))
	{
		printf("create new thread failed\n");
		return;
	}

	pthread_mutex_lock(&mutex);
	sleep(2);
	printf("main\n");
	pthread_mutex_unlock(&mutex);
	pthread_join(tid, NULL);
	
	pthread_mutex_destroy(&mutex);

	return;
}


注意:线程安全是gcc -pthread 编译

线程非安全是gcc-lpthread编译

内容概要:本文档详细介绍了基于MATLAB实现的无人机三维路径规划项目,核心算法采用蒙特卡罗树搜索(MCTS)。项目旨在解决无人机在复杂三维环境中自主路径规划的问题,通过MCTS的随机模拟与渐进式搜索机制,实现高效、智能化的路径规划。项目不仅考虑静态环境建模,还集成了障碍物检测与避障机制,确保无人机飞行的安全性和效率。文档涵盖了从环境准备、数据处理、算法设计与实现、模型训练与预测、性能评估到GUI界面设计的完整流程,并提供了详细的代码示例。此外,项目采用模块化设计,支持多无人机协同路径规划、动态环境实时路径重规划等未来改进方向。 适合人群:具备一定编程基础,特别是熟悉MATLAB和无人机技术的研发人员;从事无人机路径规划、智能导航系统开发的工程师;对MCTS算法感兴趣的算法研究人员。 使用场景及目标:①理解MCTS算法在三维路径规划中的应用;②掌握基于MATLAB的无人机路径规划项目开发全流程;③学习如何通过MCTS算法优化无人机在复杂环境中的飞行路径,提高飞行安全性和效率;④为后续多无人机协同规划、动态环境实时调整等高级应用打下基础。 其他说明:项目不仅提供了详细的理论解释和技术实现,还特别关注了实际应用中的挑战和解决方案。例如,通过多阶段优化与迭代增强机制提升路径质量,结合环境建模与障碍物感知保障路径安全,利用GPU加速推理提升计算效率等。此外,项目还强调了代码模块化与调试便利性,便于后续功能扩展和性能优化。项目未来改进方向包括引入深度强化学习辅助路径规划、扩展至多无人机协同路径规划、增强动态环境实时路径重规划能力等,展示了广阔的应用前景和发展潜力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值