在做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. Django 数据库mysql到models的映射方法

    Django自动生成models 如果数据库表已经存在,执行命令,可以自动生成Models模型,实现models与数据表的映射 >>> python manage.py inspec ...

  2. 数据库学习之二--SQL语句以及数据类型

    一.SQL语句种类: 1. DDL(Data Definition Language,数据定义语言)用来创建或者删除存储数据用的数据库以及数据库中的表;包含以下几种指令: a. CREATE:CREA ...

  3. 在做nios ii uart232 实验时出现undefined reference to `fclose'等错误。

    程序如下 #include<stdio.h> #include<string.h> #include "system.h"   int main () { ...

  4. sequence_loss的解释

    在做seq2seq的时候,经常需要使用sequence_loss这是损失函数. 现在分析一下sequence_loss这个函数到底在做什么 # coding: utf-8 import numpy a ...

  5. 内存管理3- @property 参数详解

    @property ----------------- Create two classes: Book & Student ------------------- book.m #impor ...

  6. c 语言延时函数

    /*--- 等待x毫秒 ---*/ int sleep(unsigned long x) { clock_t c1 = clock(), c2; do { ) /* 错误 */ ; } while ( ...

  7. CF1197B

    CF1197B 题意: 出n个柱子,每个柱子一个圆盘,其半径各不相同,每次只能将柱子上只有一个圆盘的移到相邻位置,问能否全部移到一个柱子上. 解法: 思路题. 如果所有盘子都能移动到同一个柱子上,那么 ...

  8. Qt for Android(一)Qt在Android手机上全屏显示

    1.在程序的编译路径下找到AndroidManifest.xml文件,具体路径如下: 2.在pro文件中添加一句话: ANDROID_PACKAGE_SOURCE_DIR = $$PWD/Androi ...

  9. 发现一个好的手机抓包工具Http Traffic

    ---恢复内容开始--- 晚上加班闲着没事,喜欢抓包,逛破解论坛,看到他们在聊Http Traffic手机抓包工具, 就下载了打算玩玩 Http Traffic: 是 HTTP 抓包调试工具 HTTP ...

  10. 会声会影x7 每次安装均会提示:已安装这个产品的另一个版本

    会声会影x7 每次安装均会提示:已安装这个产品的另一个版本 卸载C++2008 的库就行了 文章来源:刘俊涛的博客 欢迎关注,有问题一起学习欢迎留言.评论