线程的切换比进程快的多,因为它不需要切换数据区和堆
共享数据区和堆可以用来交换信息
一、线程的创建
pthread_create()函数
#include<pthread.h>
int prthread_create(pthread * thread,const pthread_attr_t * attr,void *(*start_routine)(void *),void * arg)//成功是返回0
thread: 线程id
attr: 线程属性,一般为NULL
start_routine :调用函数的地址
arg:调用函数时传参数的地址
pthread_join() 等待指定线程结束后返回
#include<pthread.h>
int pthread_join(pthread_t thread,void ** status)//成功时返回0
thread:该id表示的线程终止后才从中返回
status:线程返回值的指针地址
例子thread1.c:pthread_create pthread_join配合使用
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void * thread_main(void *arg){
int k=*((int *)arg);
int i;
for(i=0;i<k;i++)
{
sleep(1);
printf("%d\n",i );
}
return NULL;
}
int main(int argc, char const *argv[])
{
pthread_t t_id;
int thread_num=5;
void *thr_return;
if(pthread_create(&t_id,NULL,thread_main,(void *)&thread_num)<0)
{
puts("pthread_creat() error");
}
pthread_join(t_id,&thr_return);
printf("pthread return message %s\n",(char *)thr_return );
return 0;
}
编译是加上:-lpthread
g++ -o pthread thread1.c -lpthread
运行结果:
0
1
2
3
4
pthread return message (null)
二、线程存在的问题
函数分为线程安全与非线程安全的,要使用系统提供函数的线程安全函数需要添加编译宏:-D_REENTRANT()
临界区:多个线程中这样的代码块:可能同时访问某一共享变量
如:int sum=0;是一个全局变量
线程a、b都要执行sum++;有可能在线程a中还没有完成这个动作就切换到了线程b中执行sum++,这将引起错乱!
例子thread2.c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define NUM_THREAD 100//创建100个线程
void * thread_one(void *);
void * thread_two(void *);
long long num=0;
int main()
{
pthread_t thread_id[NUM_THREAD];
int i;
for(i=0;i<NUM_THREAD;i++)
{
if(i%2==0)
pthread_create(&(thread_id[i]),NULL,thread_one,NULL);
else
pthread_create(&(thread_id[i]),NULL,thread_two,NULL);
}
for(i=0;i<NUM_THREAD;i++)
pthread_join(thread_id[i],NULL);
printf("return:%lld\n",num);
return 0;
}
void *thread_one(void * arg)
{
int i;
for(i=0;i<500000;i++)
num++;
return NULL;
}
void *thread_two(void * arg)
{
int i;
for(i=0;i<500000;i++)
num--;
return NULL;
}
编译:gcc thread2.c -D_REENTRANT -o th2 -lpthread
运行4次的结果
return:2879406
return:2862240
return:2147264
return:1912373
本来的运行结果应该是0,下面我们来看如何解决?
三、线程同步
什么是同步:一个线程访问全局变量时,应该阻止其他线程的访问
需要同步的情况:
1、同时访问同一内存空间时发生的情况。
2、需要指定访问同一内存空间的线程的执行顺序的情况。
互斥量:
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t*attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
/*
临界区代码
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex);
以上函数成功时返回0;
mutex:互斥变量的地址
attr: 互斥量的属性,默认为NULL
一个例子看懂互斥量的使用:
例子thread3.c:
<pre name="code" class="cpp">#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define NUM_THREAD 100//创建100个线程
void * thread_one(void *);
void * thread_two(void *);
long long num=0;
pthread_mutex_t mutex;//
int main()
{
pthread_t thread_id[NUM_THREAD];
pthread_mutex_init(&mutex,NULL);//init
int i;
for(i=0;i<NUM_THREAD;i++)
{
if(i%2==0)
pthread_create(&(thread_id[i]),NULL,thread_one,NULL);
else
pthread_create(&(thread_id[i]),NULL,thread_two,NULL);
}
for(i=0;i<NUM_THREAD;i++)
pthread_join(thread_id[i],NULL);
printf("return:%lld\n",num);
pthread_mutex_destroy(&mutex);//destroy
return 0;
}
void *thread_one(void * arg)
{
int i;
pthread_mutex_lock(&mutex);//
for(i=0;i<500000;i++)
num++;
pthread_mutex_unlock(&mutex)//;
return NULL;
}
void *thread_two(void * arg)
{
int i;
for(i=0;i<500000;i++)
{
pthread_mutex_lock(&mutex);//
num--;
pthread_mutex_unlock(&mutex);//
}
return NULL;
}
运行结果:return :0
因为thread_two要运行500000次lock、unlock
信号量:
信号量与互斥量极为相似(此处我们只使用01信号量)
#include<semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
int sem_destroy(sem_t *sem);
int sem_post(sem_t *sem);//++
/*
临界区
*/
int sem_wait(sem_t *sem);//--
以上函数成功时返回0
sem:信号量变量地址
pshared:0在本进程内部使用,其他值可以多个进程共享
value:信号量的初始值
例子thread4.c(读入一个数,求它与sum的和,将和存在sum中,循环5次)
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define NUM_THREAD 100//创建100个线程
void * Read(void *);
void * accu(void *);
int num;
sem_t sem_one;
sem_t sem_two;
int main()
{
pthread_t id1,id2;
sem_init(&sem_one,0,0);//init
sem_init(&sem_two,0,1);
pthread_create(&id1,NULL,Read,NULL);
pthread_create(&id2,NULL,accu,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
sem_destroy(&sem_one);
sem_destroy(&sem_two);
return 0;
}
void *Read(void * arg)
{
int i;
for(i=0;i<5;i++)
{
sem_wait(&sem_two);
scanf("%d",&num);
sem_post(&sem_one);
}
return NULL;
}
void *accu(void * arg)
{
int i,sum=0;
for(i=0;i<5;i++)
{
sem_wait(&sem_one);
sum+=num;
sem_post(&sem_two);
}
printf("sum = %d\n",sum);
return NULL;
}
运行结果:
1
2
3
4
5
sum = 15
执行顺序依次为
sem_wait(&sem_two);0
sem_post(&sem_one);1
sem_wait(&sem_one);0
sem_post(&sem_two);1
Read、accu 函数交替调用且开始调用Read
四、多线程并发服务器的实现
#include<pthread.h>
int pthread_detach(pthread_t thread);
thread 要销毁的进程id