在做NDK开发的时候,很多情况下都是需要使用多线程的,一方面是提高程序运行效率,另一方面就是防止主线程阻塞

C的多线程

在C语言里,可以通过对于POSIX标准的运用,使得C语言执行多线程

提高程序的执行速度,以及对资源的合理利用

POSIX

POSIX原理

POSIX可以让C语言实现多线程

其实现是是通过POSIX函数库的调用实现的

POSIX函数库可以看作是C语言库函数的超集,对C语言尽行了增强

POSIX实现多线程

在C语言中调用POSIX库函数可以实现多线程

在使用时,需要包含pthread.h头文件

其步骤为:

  • 创建线程ID,使用pthread_t创建线程ID
pthread_t tid;
  • 创建线程,使用pthread_create()创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
  • 结束线程

    线程依附于主线程,主线程结束,其子线程也就结束了

    要使主线程等待子线程运行完毕,就需要使用pthread_join()函数
int pthread_join(pthread_t thread, void **retval);

另外,在线程运行的时候可以结束线程,可以通过以下函数结束正在运行的线程

void pthread_exit(void *retval);
int pthread_cancel(pthread_t thread);

e.g.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> void* thr_fun(void* arg){
char* buf = (char*)arg;
int i = 0;
for(; i < 100; i++){
printf("%s thread -%d-\n", buf, i);
usleep(1000);
}
return "thread over\n";
} void main(){
pthread_t tid; //创建线程ID
pthread_create(&tid, NULL, thr_fun, "pass"); //创建线程并执行thr_fun函数
int i = 0;
for(; i < 100; i++)
{
printf("main thread -%d-\n", i);
usleep(1000);
}
void* rval;
pthread_join(tid, &rval); //等待线程执行完毕
printf("get from thread:%s", (char *)rval);
}

上述示例在加入usleep是为了主线程和子线程都能够输出

注意:在编译有posix标准库多线程的时候,应该添加-lpthread参数,否则会报错

/tmp/ccEEnOE4.o: In function `main':
threadtest.c:(.text+0x7f): undefined reference to `pthread_create'
threadtest.c:(.text+0xc3): undefined reference to `pthread_join'
collect2: error: ld returned 1 exit status

互斥锁

为了线程的安全,给线程加上互斥锁,这样就可以确保线程安全

线程锁的作用就是在一个线程进行访问的时候,不允许其他线程进入

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> int count = 0;
pthread_mutex_t mutex; //声明互斥锁 void* thr_fun(void* arg){
pthread_mutex_lock(&mutex); //加锁
char *buf = (char*)arg;
for(;count < 5; count++){
printf("thread:%s, count:%d\n", buf, count);
}
count = 0;
pthread_mutex_unlock(&mutex); //解锁
} void main(){
pthread_t tid1, tid2;
pthread_mutex_init(&mutex, NULL); //初始化互斥锁 pthread_create(&tid1, NULL, thr_fun, "thread-1");
pthread_create(&tid2, NULL, thr_fun, "thread-2"); pthread_join(tid1, NULL);
pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex); //销毁互斥锁
}

生产者与消费者

  • 当存在多个线程对同一数据进行操作的时候,那么这个数据如果同时被多个线程操作,就会产生安全问题
  • 比如线程A在访问数据的时候丢失了CPU的控制权,此时线程B去操作了数据,那么线程A在重新得到CPU控制权的时候,其渠道的数据就是线程B操作过的数据,可能其数据并不是预期要取的值
  • 面对这种情况,在设计模式里面就提出了生产者与消费者模型,这也是很常用的一种模型

单个生产者与单个消费者

这种情况下,只需要满足生产者生产出来能及时被消费者消费

其生产者应该上锁,生产,通知消费者,解锁,然后按照这个流程不断循环

消费者应该上锁,消费,通知生产者,解锁,然后按照这个流程不断循环

全局变量

int ready = 0;
int product_idx = 0, consumer_idx = 0;
pthread_mutex_t mutex;
pthread_cond_t has_product;

生产者方法

void* producer(void* arg){
char *buf = (char *)arg;
while(1)
{
pthread_mutex_lock(&mutex); //上锁
ready++; //生产
product_idx++;
printf("%5d%s----%d\n", product_idx, buf, ready);
pthread_cond_signal(&has_product); //通知消费者,会阻塞
pthread_mutex_unlock(&mutex); //解锁
usleep(100 * 1000);
}
}

消费者方法

void* consumer(void* arg){
char *buf = (char *)arg;
while(1)
{
pthread_mutex_lock(&mutex); //上锁
while(ready == 0)
{
pthread_cond_wait(&has_product,&mutex); //线程等待
}
ready--; //消费
consumer_idx++;
printf("%5d%s----%d\n", consumer_idx, buf, ready);
pthread_mutex_unlock(&mutex); //解锁
usleep(100 * 1000);
}
}

主方法

void main()
{
pthread_t pro_id, con_id;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&has_product, NULL);
pthread_create(&pro_id, NULL, producer, "producer");
pthread_create(&con_id, NULL, consumer, "consumer");
pthread_join(pro_id, NULL);
pthread_join(con_id, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&has_product);
}

以上方法就实现了线程的安全,在生产者有产品的时候通知消费者,消费者完成消费等待生产者

多个生产者与多个消费者

由于上面已经说明原理,这里直接贴出代码

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> int ready = 0; #define CONSUMER_NUM 4 //消费者数量
#define PRODUCER_NUM 3 //生产者数量
pthread_t pids[CONSUMER_NUM + PRODUCER_NUM]; pthread_mutex_t mutex; //互斥锁
pthread_cond_t has_product; //条件变量 void* producer(void* arg){
int *num = (int *)arg;
while(1)
{
pthread_mutex_lock(&mutex); //上锁
ready++; //生产
printf("%5d---product---%d\n", num, ready);
pthread_cond_signal(&has_product); //通知消费者,会阻塞
pthread_mutex_unlock(&mutex); //解锁
usleep(100 * 1000);
}
} void* consumer(void* arg){
int *num = (int *)arg;
while(1)
{
pthread_mutex_lock(&mutex); //上锁
while(ready == 0)
{
pthread_cond_wait(&has_product, &mutex); //线程等待
}
ready--; //消费
printf("%5d---consumer---%d\n", num, ready);
pthread_mutex_unlock(&mutex); //解锁
usleep(100 * 1000);
}
} void main()
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&has_product, NULL); int i;
for(i = 0; i < PRODUCER_NUM; i++){ //生产者线程
pthread_create(&pids[i], NULL, producer, (void*)i);
} for(i = 0; i < CONSUMER_NUM; i++){ //消费者线程
pthread_create(&pids[PRODUCER_NUM + i], NULL, consumer, (void*)i);
} sleep(10);
for(i = 0; i < PRODUCER_NUM + CONSUMER_NUM; i++){
pthread_join(pids[i], NULL);
} pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&has_product);
}

NDK学习笔记-多线程与生产消费模式的更多相关文章

  1. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  2. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  3. Java的多线程实现生产/消费模式

    Java的多线程实现生产/消费模式 在Java的多线程中,我们经常使用某个Java对象的wait(),notify()以及notifyAll() 方法实现多线程的通讯,今天就使用Java的多线程实现生 ...

  4. tensorflow学习笔记——多线程输入数据处理框架

    之前我们学习使用TensorFlow对图像数据进行预处理的方法.虽然使用这些图像数据预处理的方法可以减少无关因素对图像识别模型效果的影响,但这些复杂的预处理过程也会减慢整个训练过程.为了避免图像预处理 ...

  5. ffmpeg学习笔记-多线程音视频解码

    之前的视频解码仍然存在问题,那就是是在主线程中去完成解码的,会造成线程阻塞,这里将其改为多线程解码,使其主线程不被阻塞 前面介绍了音视频的主线程解码,那样会阻塞主线程,在前面学习了多线程以后,就可以对 ...

  6. C++学习笔记——多线程(1)

    目前在做推理引擎开发相关的工作,这块内容的话,对工程能力的要求还是比较高的,不再像以前只是写一些Python脚本训训模型就可以了,而且深入了解C++之后,也能感受到Python较C++暴露出的缺点,另 ...

  7. NDK学习笔记-JNI多线程

    前面讲到记录到ffmpeg音视频解码的时候,采用的是在主线程中进行操作,这样是不行的,在学习了POSIX多线程操作以后,就可以实现其在子线程中解码了,也可以实现音视频同步了 简单示例 在native实 ...

  8. python学习笔记- 多线程(1)

    学习多线程首先先要理解线程和进程的关系. 进程 计算机的程序是储存在磁盘中的可执行的二进制文件,执行时把这些二进制文件加载到内存中,操作系统调用并交给处理器执行对应操作,进程是程序的一次执行过程,这是 ...

  9. NDK学习笔记(四):OutputContext机制

    首先NDK文档中的Op.h头文件中已经有了相关概念的解释,摘录翻译如下: /*! \fn const OutputContext& Op::outputContext() const; The ...

随机推荐

  1. Codeforces Round #584 B. Koala and Lights

    链接: https://codeforces.com/contest/1209/problem/B 题意: It is a holiday season, and Koala is decoratin ...

  2. python自动华 (五)

    Python自动化 [第五篇]:Python基础-常用模块 目录 模块介绍 time和datetime模块 random os sys shutil json和pickle shelve xml处理 ...

  3. 更改ejs模板引擎的后缀为html

    安装 EJS 在项目目录右键->Open Command Prompt Here 输入 npm install ejs 打开app.js //app.set('view engine', 'ja ...

  4. django获取数据

    获取单个值 request.POST.get('user') # user对应前端name属性对应的值 获取多个值(如checkbox,multiple) request.POST.getlist(' ...

  5. Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises) C题

    C. Bad Sequence Problem Description: Petya's friends made him a birthday present — a bracket sequenc ...

  6. MongoDB 运维实总结

    一.MongoDB 集群简介 MongoDB是一个基于分布式文件存储的数据库,其目的在于为WEB应用提供可扩展的高性能数据存储解决方案.下面将以3台机器介绍最常见的集群方案.具体介绍,可以查看官网 h ...

  7. scrapy框架之spider

    爬取流程 Spider类定义如何爬取指定的一个或多个网站,包括是否要跟进网页里的链接和如何提取网页内容中的数据. 爬取的过程是类似以下步骤的循环: 1.通过指定的初始URL初始化Request,并指定 ...

  8. Ubuntu常用命令及git常用命令

    1. CMakeLists.txt中指定OpenCV路径 set(OPENCV_DIR /***/***/opencv-2.4.9) 2. cmake工程编译安装 mkdir build cd bui ...

  9. pycham更换主题

    在pycham的file-->Setting-->Editor-->Colors&Fonts

  10. JAVA基础知识|HTTP协议-两个特性

    一.无连接 无连接:服务器与浏览器之间的一次连接只处理一个http请求,请求处理结束后,连接断开.下一次请求再重新建立连接. 然而随着互联网的发展,一台服务器同一时间处理的请求越来越多,如果依然采用原 ...