在使用多线程时,首先将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、分离线程
默认情况下,创建的线程是非分离线程;所谓非分离线程,就是创建的子线程,可以在主线程或者其他线程中,操作该线程的优先级join
、sleep
等等;
分离线程就是不能被其他的线程操作,像join操作等等,都不可以。
pthread_attr_t attr;
pthread_attr_init(&attr);
//设置为分离线程
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
使用pthread_attr_setdetachstate
可以设置线程为分离线程,使用分离线程,那么就不能操作子线程。
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就会被释放。