在做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. asp.net用sql数据库生成json字符串并显示出来

    use Shop ,) )) insert into DictBase select '包装' UNION ALL select '价格' UNION ALL select '品牌' 工厂方法模式 I ...

  2. BZOJ 5494: [2019省队联测]春节十二响 (左偏树 可并堆)

    题意 略 分析 稍微yy一下可以感觉就是一个不同子树合并堆,然后考场上写了一发左偏树,以为100分美滋滋.然而发现自己傻逼了,两个堆一一对应合并后剩下的一坨直接一次合并进去就行了.然鹅我这个sb把所有 ...

  3. jenkins 邮件发送错误

    jenkins 在创建新的 Build 的时候希望邮件进行通知. 但是邮件通知的时候出现错误: Unable to Send Mail - javax.net.ssl.SSLException: Un ...

  4. Django-常用异常

    1 from rest_framework.authentication import BasicAuthentication raise AuthenticationFailed(res.dict) ...

  5. element-ui下拉按钮的用法

    <el-dropdown class="avatar-container" trigger="click"> <div class=" ...

  6. spring cloud学习

    https://gitee.com/ylimhhmily/SpringCloudTutorial/tree/master hystrix配置不生效问题https://blog.csdn.net/lvy ...

  7. ubuntu mysql 的安装、配置、简单使用,navicat 连接

    MySQL 的安装 1. 先更新 apt 安装中心: apt update 里面会有默认最新的mysql 的包. 2.安装msyql : sudo apt-get install mysql-serv ...

  8. Flutter移动电商实战 --(28)列表页_商品列表后台接口调试

    主要调试商品列表页的接口 这个接口是最难的因为有大类.小类还有上拉加载 先配置接口 config/service_url.dart //const serviceUrl='http://test.ba ...

  9. 使用badusb“烧鹅”制作“百度U盘”

    HID攻击:USB HID攻击技术是一种利用USB接口伪造用户击键行为实施是攻击的方式.通过恶意USB HID设备连接主机后发送伪造的按键命令,篡改系统设置.运行恶意功能.这种技术区别于传统的USB攻 ...

  10. tensorflow查看ckpt各节点名称

    from tensorflow.python import pywrap_tensorflowimport os checkpoint_path=os.path.join('output/res101 ...