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);
    }
    
  • 相关阅读:
    Nginx负载均衡:分布式/热备Web Server的搭建
    CentOS6.6 32位 Minimal版本纯编译安装Nginx Mysql PHP Memcached
    windows下nginx安装、配置与使用
    Redis基本操作——List
    MongoDB aggregate 运用篇 个人总结
    构建一个较为通用的业务技术架构
    2016年31款轻量高效的开源JavaScript插件和库
    正则表达式
    前端学习路线
    可变参数
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664651.html
Copyright © 2011-2022 走看看