- 创建进程程的效率低,
- 进程共享数据不方便
- 进程的代码比较冗余
因此有时需要线程
如何创建线程
使用流行的线程库:POSIX线程库,也叫pthread。
假设独立线程运行两个函数
void* does_not(void *a)
{
for(int i = 0;i < 5; i++){
sleep(1);
puts("Does not")
}
return NULL;
}
void* does_too(void*a)
{
for(int i = 0;i < 5; i++){
sleep(1);
puts("Does too")
}
return NULL;
}
void指针是可以指向存储器中任何类型的数据。
使用pthread_create创建线程
头文件和函数声明
#include<pthread.h>
...
int pthread_create(pthread_t *tidp,
const pthread_attr_t *attr,
(void*)(*start_rtn)(void*),
void *arg);
- 第一个参数为指向线程标识符的指针。
- 第二个参数用来设置线程属性。
- 第三个参数是线程运行函数的起始地址。
- 最后一个参数是运行函数的参数。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>//pthread库的头文件
void error(char *msg)
{
fprintf(stderr,"%s:%s\n",msg.strerror(errno));
exit(1);
}
每个线程都需要把信息保存在一个叫pthread_t的数据结构中,然后用pthread_create()创建并运行线程。
pthread_t t0;
pthread_t t1;
if(prhread_greate(&t0,NULL,does_not,NULL) == -1)
error("无法创建线程t0");
if(prhread_greate(&t1,NULL,does_too,NULL) == -1)
error("无法创建线程t1");
代码将以独立线程运行这两个函数,程序运行完这段代码,线程也会随之灭亡,因此必须等待线程结束。
void* result;
//函数返回的void指针回保存在这里
if(pthread_join(t0,&result) == -1)
error("无法回收线程t0");
if(pthread_join(t1,&result) == -1)
error("无法回收线程t1");
pthread_join()会接收线程函数的返回值,并把它保存在void指针变量中,一旦线程都结束,程序就可以顺利退出。
互斥锁
线程之间共享数据,因此需要互斥锁,来保护某段代码的安全。
pthread_mutex_t a_lock = PTHREAD_MUTEX_INITIALIZER;
互斥锁必须对所有有可能发生冲突的线程可见,也就是说它是一个全局变量
PTHREAD_MUTEX_INITIALIZER是一个宏,当编译器看到它,就会插入创建互斥锁的代码。
1、红灯停
pthread_mutex_lock(&a_lock);
//一次只允许一个线程通过这里.
//其他线程运行到这里必须等待
/*含有共享数据的代码从这里开始*/
2、绿灯行
/*含有共享数据的代码结束*/
pthread_mutex_umlock(&a_local);
void指针参数
线程函数可以接受一个void指针作参数并返回一个void指针值,因为long的大小和void指针大小相同,可以把它保存在void指针变量中
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>//pthread库的头文件
void error(char *msg)
{
fprintf(stderr,"%s:%s\n",msg,strerror(errno));
exit(1);
}
//线程函数接收一个void指针类型的参数
void* do_stuff(void* param)
{
long thread_no = (long)param;//把它强子转化long
printf("Thread number %ld\n",thread_no);
return(void*)(thread_no + 1);
}
int main()
{
pthread_t threads[3];
long t;
for(t = 0; t < 3; t++ ){
if(pthread_create(&threads[t],NULL,do_stuff,(void*)t) == -1)
error("创建线程是失败");
//将long型指针变量t转化为void指针
}
void* result;
for(t = 0; t < 3;t++){
if(pthread_join(threads[t],&result) == -1)
error("无法回收线程");
printf("Thread %ld return %ld\n",t,(long)result);
}
return 0;
}
运行:
#./param_test
Thread number 0
Thread number 1
Thread number 2
Thread 0 return 1
Thread 1 return 2
Thread 2 return 3
练习代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>//pthread库的头文件
int beers = 2000000;
pthread_mutex_t beers_lock = PTHREAD_MUTEX_INITIALIZER;
void error(char *msg)
{
fprintf(stderr,"%s:%s\n",msg,strerror(errno));
exit(1);
}
//线程函数接收一个void指针类型的参数
void* drink_lots(void* a)
{
int i;
pthread_mutex_lock(&beers_lock);
for(i = 0; i < 100000; i++)
beers = beers - 1;
pthread_mutex_unlock(&beers_lock);
printf("beers = %i\n", beers);
return NULL;
}
int main()
{
pthread_t threads[20];
int t;
printf("%i bottles of bear on the wall\n%i bottles of beer\n",beers,beers );
for(t = 0; t < 20; t++ ){
if(pthread_create(&threads[t],NULL,drink_lots,NULL) == -1)
error("创建线程失败");
}
void* result;
for(t = 0; t < 20;t++){
if(pthread_join(threads[t],&result) == -1)
error("回收线程失败");
}
printf("There are now %i bottles of beer on the wall\n", beers);
return 0;
}
版本2:
...
void* drink_lots(void* a)
{
int i;
for(i = 0; i < 100000; i++){
pthread_mutex_lock(&beers_lock);
beers = beers - 1;
pthread_mutex_unlock(&beers_lock);
}
printf("beers = %i\n", beers);
return NULL;
}
...
- 线程会让程序变得更快,但要孔子代码中锁的数量,如果锁太多,就会像单线程一样慢。
- 创建进程要比创建线程花更多的时间。
- 互斥锁会引发『死锁』










网友评论