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);
}
原文地址:https://www.cnblogs.com/cj5785/p/10664651.html