Android NDK开发-----C++基础(多线程、智能指针)

在使用多线程时,首先将pthread配置到项目中,这部分和配置ffmpeg环境一致。

1、pthread环境配置

project ("CMakeProject12")
#所用到的头文件  include文件夹
include_directories("E:/新建文件夹/ndk/pthreads-w32-2-9-1-release/Pre-built.2/include")
#获取动态库  lib文件夹
link_directories("E:/新建文件夹/ndk/pthreads-w32-2-9-1-release/Pre-built.2/lib/x64")

# 将源代码添加到此项目的可执行文件。
add_executable (CMakeProject12 "CMakeProject12.cpp" "CMakeProject12.h")
#添加动态库的可执行文件 lib文件夹中的.lib库
target_link_libraries(CMakeProject12 pthreadVC2)

配置完环境之后,Ctrl + S保存CMakeList.txt文件,在头文件中引入pthread就可以使用线程。

调试过程中,出现未找到pthreadVC2.dll动态库文件,需要去Windows/System32文件夹下添加dll文件。

2、线程的创建

	pthread_t pid;
	int i = 100;
	pthread_create(&pid,0,thread_task,&i);

线程的创建,需要使用pthread_create创建,使用pthread_create需要传入4个参数,第一个参数是pthread_t 的指针,第二个参数先默认给0;第三个参数和第四个参数是匹配的 ,参数3是函数指针,传入void返回void,参数4就是要传入的void值。

void* thread_task(void* args) {
	int i = *static_cast<int*>(args);
	cout << "线程参数:" << i << endl;
	return 0;
}

使用join可使线程执行完毕再执行主线程代码。

	pthread_join(pid, 0);
	cout << "线程执行完毕" << endl;

在这里插入图片描述
下面详细介绍一下各个参数。

参数2:线程属性,pthread_attr_t

	pthread_attr_t attr;
	//初始化
	pthread_attr_init(&attr);
	//销毁
	pthread_attr_destroy(&attr);

3、分离线程

默认情况下,创建的线程是非分离线程;所谓非分离线程,就是创建的子线程,可以在主线程或者其他线程中,操作该线程的优先级joinsleep等等;

分离线程就是不能被其他的线程操作,像join操作等等,都不可以。

	pthread_attr_t attr;
	pthread_attr_init(&attr);
	//设置为分离线程
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

使用pthread_attr_setdetachstate可以设置线程为分离线程,使用分离线程,那么就不能操作子线程。
Android NDK开发-----C++基础(多线程、智能指针)_第1张图片
4、线程的同步

假设创建10个线程;

queue<int> q;

void* thread_task1(void* args) {
	if (!q.empty()) {
		printf("获取队列数据:%d", q.front());
		q.pop();
	}
	else {
		printf("未获取队列数据");
	}
	return 0;
}

void test() {
	for (size_t i = 0; i < 5; i++)
	{
		q.push(i);
	}

	pthread_t pid[10];
	//创建10个线程
	for (size_t i = 0; i < 10; i++)
	{
		pthread_create(&pid[i], 0, thread_task1, &i);
	}
}

现在的情况是10个线程去获取队列中的数据,目前还未做同步。10个线程同时竞争获取队列中的资源,这样必然会出现线程安全问题。

在Java中,需要加锁完成线程同步,在C++中提供了互斥锁pthread_mutex_t

pthread_mutex_t mutex;

//初始化锁
pthread_mutex_init(&mutex,0);
......
//销毁锁
pthread_mutex_destroy(&mutex);

在线程中需要加锁

void* thread_task1(void* args) {
	//加锁
	pthread_mutex_lock(&mutex);
	if (!q.empty()) {
		printf("获取队列数据:%d", q.front());
		q.pop();
	}
	else {
		printf("未获取队列数据");
	}
	//解锁
	pthread_mutex_unlock(&mutex);
	return 0;
}

5、手写线程安全的队列

通过前面对于线程同步的安全处理方式,手写一个线程安全的队列。

template typename T;
class SafeQueue {
private:
	queue<T> q;
	//锁
	pthread_mutex_t mutex;
	//Java的 wait notify  条件变量
	pthread_cond_t cond;

public:
	SafeQueue() {
		//初始化
		pthread_mutex_init(&mutex, 0);
		pthread_cond_init(&cond, 0);
	}
	~SafeQueue() {
		//销毁
		pthread_mutex_destroy(&mutex);
		pthread_cond_destroy(&cond);
	}
	void push(T t) {
		//加锁
		pthread_mutex_lock(&mutex);
		q.push(t);
		pthread_mutex_unlock(&mutex);
	}

	void pop(T &t) {
		pthread_mutex_lock(&mutex);
		if (!q.empty()) {
			t = q.front();
			q.pop();
		}
		pthread_mutex_unlock(&mutex);
	}
};

以上就是简单的实现线程安全的队列,但是如果我们想要在队列为空的时候,一直等到队列中有数据的时候,就取出数据,那么可以设置条件变量pthread_cond_t

void push(T t) {
		//加锁
		pthread_mutex_lock(&mutex);
		q.push(t);
		//当完成push操作的时候,就唤醒 notify
		pthread_cond_signal(&cond);
		//广播唤醒所有 notifyAll
		pthread_cond_broadcast(&cond);
		
		pthread_mutex_unlock(&mutex);
	}

	void pop(T &t) {
		pthread_mutex_lock(&mutex);
		/*if (!q.empty()) {
			t = q.front();
			q.pop();
		}*/
		//Java的wait效果
		while (q.empty()) {
			pthread_cond_wait(&cond, &mutex);
		}
		t = q.front();
		q.pop();
		pthread_mutex_unlock(&mutex);
	}

在main函数中使用。

SafeQueue<int> sq;


void* push_task(void* args) {
	while (1) {
		int i;
		cin >> i;
		sq.push(i);
	}
}

void* pop_task(void* args) {
	while (1) {
		int i;
		sq.pop(i);
		cout << "取出数据:" << i << endl;
	}
}

void main1() {
	//创建2个线程
	pthread_t pid1, pid2;
	pthread_create(&pid1, 0, push_task, 0);
	pthread_create(&pid2, 0, pop_task, 0);
}

6、智能指针

一般来说,当在栈中创建一个引用对象,当使用完毕之后,就会被释放。

class A {
public:
	A() {

	}
	~A() {
		cout << "被释放" << endl;
	}
};

void test() {
	A a;
}

当调用test方法之后,a会被释放;当使用new关键字动态申请内存的时候,就需要手动释放。

	A *a = new A;
	//delete手动释放
	delete(a);

但是当使用智能指针的时候,就可以不用手动释放动态分配的内存。

	A *a = new A;
	shared_ptr<A> sp(a);    //a  引用计数 1
	shared_ptr<A> sp(a);    //a  引用计数 2

使用shared_ptr来修饰指针a,同样也可以释放a。

其中内部的原理就是,在使用shared_ptr来修饰a时,a的引用计数会加1,以此类推,当shared_ptr指针出栈的时候,引用计数会-1,全部出栈则a的引用计数为0,所以a就会被释放。

你可能感兴趣的:(Android NDK开发-----C++基础(多线程、智能指针))