在做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. C# 读取、写入文件

    读取文件 File.ReadAllText(textBox1.Text,Encoding.ASCII); Form namespace ReadWriteText { public partial c ...

  2. Maven+Docker 部署

    Maven+Docker 部署 安装jdk8镜像 docker pull openjdk:8-jdk-alpine maven插件推送方式 修改/etc/docker/daemon.json文件,加入 ...

  3. 002_UCOSIII任务创建于删除

    (一)先创建一个启动任务来进行创建其它任务,创建任务的宏定义 #define START_TASK_PRIO 3 //任务优先级 #define START_STK_SIZE 128 //任务堆栈大小 ...

  4. python django 连接 sql-server

    1.准备工作 python3.6连接sqlserver数据库需要引入pymssql模块 pymssql官方:https://pypi.org/project/pymssql/ 没有安装的话需要: pi ...

  5. java获取web项目下文件夹的路径方法

    方法一: String realPath=request.getSession().getServletContext() .getRealPath("upload"); 方法二: ...

  6. 【csp模拟赛3】bridge.cpp--矩阵加速递推

    题目描述 穿越了森林,前方有一座独木桥,连接着过往和未来(连接着上一题和下一题...). 这座桥无限长. 小 Q 在独木桥上彷徨了.他知道,他只剩下了 N 秒的时间,每一秒的时间里,他会向 左或向右移 ...

  7. DEVICE_ATTR设置设备属性

    DEVICE_ATTR设置设备属性 为了在sysfs下生成可控节点,方便上层调用. sysfs是一个基于RAM的文件系统,它和Kobject一起,可以将Kernel的数据结构导出到用户空间,以文件目录 ...

  8. SpringMVC框架下Web项目的搭建与部署

    这篇文章已被废弃. 现在,Deolin使用Maven构建项目,而不是下载Jar文件,使用Jetty插件调试项目,而不是外部启动Tomcat. SpringMVC比起Servlet/JSP方便了太多 W ...

  9. Python使用grequests并发发送请求

    目录 前言 grequests简单使用 grequests和requests性能对比 异常处理 前言 requests是Python发送接口请求非常好用的一个三方库,由K神编写,简单,方便上手快.但是 ...

  10. 两大主流开源分布式存储的对比:GlusterFS vs. Ceph

    两大主流开源分布式存储的对比:GlusterFS vs. Ceph 存储世界最近发生了很大变化.十年前,光纤通道SAN管理器是企业存储的绝对标准,但现在的存储必须足够敏捷,才能适应在新的基础架构即服务 ...