zoukankan      html  css  js  c++  java
  • NDK学习笔记-多线程与生产消费模式

    在做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-
    ", buf, i);
    		usleep(1000);
    	}
    	return "thread over
    ";
    }
    
    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-
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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);
    }
    
  • 相关阅读:
    单例模式
    Curator Zookeeper分布式锁
    LruCache算法原理及实现
    lombok 简化java代码注解
    Oracle客户端工具出现“Cannot access NLS data files or invalid environment specified”错误的解决办法
    解决mysql Table ‘xxx’ is marked as crashed and should be repaired的问题。
    Redis 3.0 Cluster集群配置
    分布式锁的三种实现方式
    maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令
    How to Use Convolutional Neural Networks for Time Series Classification
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664650.html
Copyright © 2011-2022 走看看