在做NDK开发的时候,很多情况下都是需要使用多线程的,一方面是提高程序运行效率,另一方面就是防止主线程阻塞
C的多线程
在C语言里,可以通过对于POSIX
标准的运用,使得C语言执行多线程
提高程序的执行速度,以及对资源的合理利用
POSIX
POSIX原理
POSIX
可以让C语言实现多线程
其实现是是通过POSIX
函数库的调用实现的
POSIX函数库可以看作是C语言库函数的超集,对C语言尽行了增强
POSIX实现多线程
在C语言中调用POSIX库函数可以实现多线程
在使用时,需要包含pthread.h
头文件
其步骤为:
创建线程ID,使用
pthread_t
创建线程ID1
pthread_t tid;
创建线程,使用
pthread_create()
创建线程1
2int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);结束线程
线程依附于主线程,主线程结束,其子线程也就结束了
要使主线程等待子线程运行完毕,就需要使用pthread_join()
函数1
int pthread_join(pthread_t thread, void **retval);
另外,在线程运行的时候可以结束线程,可以通过以下函数结束正在运行的线程1
2void pthread_exit(void *retval);
int pthread_cancel(pthread_t thread);
e.g.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
参数,否则会报错1
2
3
4/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
互斥锁
为了线程的安全,给线程加上互斥锁,这样就可以确保线程安全
线程锁的作用就是在一个线程进行访问的时候,不允许其他线程进入1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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操作过的数据,可能其数据并不是预期要取的值
- 面对这种情况,在设计模式里面就提出了生产者与消费者模型,这也是很常用的一种模型
单个生产者与单个消费者
这种情况下,只需要满足生产者生产出来能及时被消费者消费
其生产者应该上锁,生产,通知消费者,解锁,然后按照这个流程不断循环
消费者应该上锁,消费,通知生产者,解锁,然后按照这个流程不断循环
全局变量1
2
3
4int ready = 0;
int product_idx = 0, consumer_idx = 0;
pthread_mutex_t mutex;
pthread_cond_t has_product;
生产者方法1
2
3
4
5
6
7
8
9
10
11
12
13void* 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);
}
}
消费者方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void* 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);
}
}
主方法1
2
3
4
5
6
7
8
9
10
11
12void 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);
}
以上方法就实现了线程的安全,在生产者有产品的时候通知消费者,消费者完成消费等待生产者
多个生产者与多个消费者
由于上面已经说明原理,这里直接贴出代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
int ready = 0;
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);
}