每个进程的内存空间都有保存全局变量的“数据区”,动态分配内存空间的堆,函数运行时使用的栈。
进程的切换需要上下文切换,开销巨大。
线程共享数据区和堆,只需分离栈区域,上下文切换时开销较小。
下面是多线程求1-10的和的程序:
thread.c
//多进程模型缺点:创建进程过程会带来一定开销(频繁的上下文切换);数据交换需要IPC技术
//上下文切换:如果运行进程A后要切换到运行进程B,就应将A的相关信息移出内存,并读入B的相关信息
//多线程模型优点:上下文切换不需要切换数据区和堆,可以利用数据区和堆交换数据
#include
#include
void * thread_summation(void *arg);
int sum=0;
int main(void)
{
pthread_t id_t1, id_t2; //线程ID
int range1[]={1, 5};
int range2[]={6, 10};
//创建线程
pthread_create(&id_t1, NULL, thread_summation, (void*)range1);
pthread_create(&id_t2, NULL, thread_summation, (void*)range2);
//等待线程终止
pthread_join(id_t1, NULL);
pthread_join(id_t2, NULL);
printf("result: %d \n", sum);
return 0;
}
void* thread_summation(void* arg)
{
int start=((int *)arg)[0];
int end=((int *)arg)[1];
while(start<=end)
{
sum+=start; //此处存在临界区相关问题
start++;
}
return NULL;
}
程序执行流程图如下图所示:
但是上述程序存在一个问题:
“2个线程会同时访问全局变量sum”
如下图所示,线程1首先读取该变量num的值并将其传给CPU,获得加1后的结果是100,最后再把结构写回变量num,这样num中就保存100。
但是线程2有可能在线程1还未写入num时拿到num(此时num为99),这样一来最终num的结果就不是101,而是100了(线程12都将100写入num)
操作num的这部分代码就叫临界区,临界区:函数内同时运行多个线程引起问题的多条语句构成的代码块。
解决上述问题的方法就是——线程同步
它解决了两个问题:1.同时访问同一内存空间发生的情况 2.需要指定访问同一内存空间的线程执行顺序的情况
针对问题1——互斥量
互斥量相当于对临界区加了一把锁,阻止多个线程同时访问。
mutex.c
//临界区:函数内同时运行多个线程时引起问题的多条语句构成的代码块
//多个线程同时访问临界区会出问题
//解决方法——线程同步:
//1.同时访问同一内存空间时发生的问题——互斥量
//2.需要指定访问同一内存空间的线程执行顺序的情况——信号量
//互斥量好比一个洗手间,同一时刻只允许一个人使用,有加锁和解锁的过程
#include
#include
#include
#include
#include
#define NUM_THREAD 100
void * thread_inc(void *arg);
void * thread_des(void *arg);
long long num=0;
pthread_mutex_t mutex; //创建互斥量
int main(void)
{
pthread_t thread_id[NUM_THREAD];
pthread_mutex_init(&mutex, NULL); //初始化
int i;
for(i=0;i
针对问题2——信号量
控制访问顺序的同步,参考操作系统中的“生产者——消费者”问题。
semaphore.c
//信号量:控制线程访问顺序的同步
//PV操作:信号量在调用sem_post时增1,调用sem_wait时减1
//在信号量为0时调用sem_wait时线程将进入阻塞状态
//生产者与消费者模型
#include
#include
#include
void * read(void *arg);
void * accu(void *arg);
//定义信号量
static sem_t sem_one;
static sem_t sem_two;
static int num;
int main(void)
{
pthread_t id_t1, id_t2;
sem_init(&sem_one, 0, 0); //初始化sem_one为0
sem_init(&sem_two, 0, 1); //初始化为1
pthread_create(&id_t1, NULL, read, NULL);
pthread_create(&id_t2, NULL, accu, NULL);
pthread_join(id_t1, NULL);
pthread_join(id_t2, NULL);
sem_destroy(&sem_one);
sem_destroy(&sem_two);
return 0;
}
void* read(void* arg)
{
int i;
for(i=0;i<5;i++)
{
fputs("Input number: ", stdout);
sem_wait(&sem_two); //占用盘子sem_two=0
//临界区
scanf("%d", &num);
sem_post(&sem_one); //在盘子上放苹果sem_one=1
}
}
void* accu(void* arg)
{
int sum=0, i;
for(i=0;i<5;i++)
{
sem_wait(&sem_one); //吃掉苹果sem_one=0
sum+=num;
sem_post(&sem_two); //放出盘子sem_two=1
}
printf("result: %d \n", sum);
return NULL;
}