前面章节中介绍了进程。从这一章开始介绍线程。进程和线程的差别是什么呢:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
4.优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
用一个比喻来形容进程和线程。一个工厂里面是由多个车间组成。进程就好比工厂的车间。它代表CPU所能处理的单个任务。但是一个车间里可以有多个工人,他们协同完成车间里面的任务。这些工人就可以看做是线程。因此一个进程里面可以包含多个线程。
车间里面的空间都是工人们共享的,这就代表一个进程的内存空间是共享的。每个线程都可以使用这些共享内存。
但是有可能车间里的某个房间空间有限,只能容纳一个人,比如厕所,里面有人的时候其他人就不能进去,这就代表线程使用某些共享内存的时候,其他线程必须等它结束才能使用这块内存。
为了防止其他人进入房间,就是在房间上加一把锁,先到的人锁上门,后到的人看到上锁,就在门口排队。等锁打开再进去,这就是互斥锁,防止多个线程同时读写某一个块内存区域。
还有些房间可以容纳n个人,如果人数大于n,多出来的人只能在外面等着,就好比某些内存只能给固定数目的线程使用。解决的办法就是在门口挂n把钥匙,进入的人取一把钥匙,出来的时候再把钥匙挂回原处。后到的人发现钥匙架空了就知道必须在门口排队等待。这种做法就叫做信号量。用来保证多个线程不会互相冲突。
下面来看下线程的具体用法:
就像每个进程有一个进程ID一样,每个线程也有一个线程ID。和进程ID在整个系统中是唯一的不同,线程ID只有在它所属的进程上下文中才有意义
线程可以通过pthread_self函数来获得自身的线程ID
#include <pthread.h>
pthread_t pthread_self(void);
当一个程序启动时,就有一个进程被操作系统创建,与此同时一个线程也立刻运行,这个线程就是程序的主线程。如果需要再创建子线程,那么创建的线程就是这个主线程的子线程。主线程把新的作业放到一个工作队列中,主线程不允许每个线程任意处理从队列顶端取出的作业,而是由主线程控制作业的分配。
线程创建:
#include <pthread.h>
pthread_t pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *),void *restrict arg);
start_rtn: 新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数有一个以上,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为art参数传入
来看一个实际的代码:
#include <stdio.h>
#include <stdlib.h>
#include "func.h"
#include <pthread.h>
#include <unistd.h>
#include <windows.h>
void printids(const char *s){
pid_t pid;
pthread_t tid;
pid=getpid();
tid=pthread_self();
printf("%s pid %lu tid %lu (0x%lx) ",s,(unsigned long)pid,(unsigned long)tid,(unsigned long)tid);
}
void *thr_fn(void *arg){
printids("new thread:");
return((void *)0);
}
pthread_t ntid;
int main()
{
int err;
err=pthread_create(&ntid,NULL,thr_fn,NULL);
printids("main thread:");
Sleep(1);
return 0;
}
运行如下:可以看到两个线程的PID都是一样的,不同的是tid
那么线程什么时候退出呢,单个线程有3种方式退出,因此可以在不终止整个进程的情况下,停止它的控制流
1 线程可以简单地从启动例程中返回,返回值是线程的退出码
2 线程可以被同一进程的其他线程取消
3 线程调用pthread_exit
来看下进程中的其他线程如何来取消线程,这就需要用到pthread_join(pthread_t thread, void **rval_ptr)
运行后调用线程一直阻塞,直到指定的线程调用pthread_exit, 退出或者返回
#include <stdio.h>
#include <stdlib.h>
#include "func.h"
#include <pthread.h>
#include <unistd.h>
#include <windows.h>
void *thr_fn2(void *arg){
printf("thread2 exiting ");
return((void *)2);
}
void *thr_fn1(void *arg){
printf("thread1 returning ");
return((void *)1);
}
int main()
{
pthread_t tid1,tid2;
void *tret;
pthread_create(&tid1,NULL,thr_fn1,NULL);
pthread_create(&tid2,NULL,thr_fn2,NULL);
pthread_join(tid1,&tret);
printf("thread1 exit code %ld ",(long)tret);
pthread_join(tid2,&tret);
printf("thread2 exit code %ld ",(long)tret);
return 0;
}
运行结果: