创建进程的工作本身会给操作系统带来沉重的负担,而且每个进程具有独立的内存空间,因此进程间通信的实现难度也不低。其主要缺点如下:
1.创建进程的过程会带来一定的开销
2.为了完成进程间数据交换,需要特殊的IPC技术
另外每秒数十次、甚至数千次的‘上限文切换’是创建进程时的最大的开销。
单CPU系统将CPU时间分成多个微小的块后分配给了多个进程。为了分时使用CPU,需要‘上下文切换’过程。
上下文切换:运行程序前需要将相应进程信息读入内存,如果运行进程A后需要紧接着运行进程B,就应该将进程A相关信息移出内存,并读入进程B相关信息。这就是上下文切换。但此时进程A的数据将被移动到硬盘,所以上下文切换需要很长时间。
为了保持多进程的优点,同时在一定程度上克服其缺点,人们引入了线程。线程相比进程具有如下优点:
1.线程的创建和上下文切换比进程的创建和上下文切换更快。
2.线程间交换数据时无需特殊技术。
线程和进程的差异
进程间的上下文切换需要复制整个内存区域,而线程则不是。每个进程的内存空间都由保存全局变量的数据区、向malloc等函数的动态分配提供空间的堆、函数运行时使用的栈构成,每个进程都拥有这种独立空间。而线程则是以获得多个代码执行流为目的,只分离栈区域,通过这种方式可以获得如下优势:
1.上下文切换时不需要切换数据区和堆
2.可以利用数据区和堆交换数据
线程为了保持多条代码执行流而隔开了栈区域,多个线程将共享数据区和堆。为了保持这种结构,线程将在进程内创建并运行。因此进程和线程可以定义为如下形式:
进程:在操作系统构成单独执行流的单位
线程:在进程构成单独执行流的单位
线程创建及运行
线程具有单独的执行流,因此需要单独定义线程的main函数,还需要请求操作系统在单独的执行流中执行该函数,完成该功能的函数如下:
#include<pthread.h>
int pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,void *(*start_routine)(void*),void *restrict arg);
//成功时返回0,失败时返回其他值
thread---保存新创建线程ID的变量地址值。线程与进程相同,也需要用于区分不同线程的ID
attr---用于传递线程属性的参数,传递NULL时,创建默认属性的线程
start_routine---相当于线程main函数的、在单独执行流中执行的函数地址值
arg---通过第三个参数传递调用函数时包含传递参数信息的变量地址值
下面是一个示例:
#include<stdio.h>
#include<pthread.h>
void* thread_main(void *arg);
int main(int arg,char **argv)
{
pthread_t t_id;
int thread_param=5;
if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)//请求创建一个线程
{
puts("pthread_create() error");
return -1;
};
sleep(10);//调用sleep函数延迟进程的终止时间
puts("end of main");
return 0; //终止进程,同时终止内部创建的线程
}
void* thread_main(void *arg) //arg参数是pthread_create函数的第四个参数
{
int i;
int cnt=*((int*)arg);
for(i=0;i<cnt;i++)
{
sleep(1);
puts("running thread");
}
return NULL;
}
关于sleep函数的调用是控制线程的执行,相当于预测程序的执行流程。实际上是不可能预测的,因此为了准确预测thread_main函数的运行时间,利用下面的函数控制线程的执行流:
#include<pthread.h>
int pthread_join(pthread_t thread,void **status);
//成功时返回0,失败时返回其他值
thread---该参数值ID的线程终止后才会从该函数返回
status---保存线程的main函数返回值的指针变量地址值
调用该函数的进程(或线程)将进入等待状态,直到第一个参数为ID的线程终止为止。而且可以得到线程的main函数的返回值。通过下面示例了解该函数的功能:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
void *thread_main(void *arg);
int main()
{
pthread_t t_id;
int thread_param=5;
void *thr_ret;
if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)//请求创建一个线程
{
puts("pthread_create() error");
return -1;
}
if(pthread_join(t_id,&thr_ret)!=0) //main函数将等待ID保存在t_id变量中的线程终止
{
puts("pthread_create() error");
return -1;
}
printf("Thread return message:%s \n",(char*)thr_ret);
free(thr_ret);
return 0;
}
void* thread_main(void *arg)
{
int i;
int cnt=*((int*)arg);
char *msg=(char*)malloc(sizeof(char)*50);
strcpy(msg,"Hello,I am thread~ \n");
for(i=0;i<cnt;i++){
sleep(1);
puts("running thread");
}
return (void*)msg; //该返回值是thread_main函数内部动态分配的内存空间地址值
}