线程的概念
线程是操作系统能够惊醒运算调度的最小单位。线程是在程序内部运行的。即线程是在进程的地址空间内运行的。
在linux中,实际上并没有确切的真正的线程。在linux利用进程来模拟线程。但是在CPU看来,会比传统的进程更加轻量化。
透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。
线程的优点
创建一个新的线程的代价比创建一个新的进程小;
线程之间的切换 相比进程间的切换 操作系统的工作会小很多;
在等待慢速I/O操作的时候,程序可以执行其他的任务;
线程占用的资源比进程少;
线程的缺点
线程可能会造成性能损失,增加额外的同步和调度开销;
因为在同一个进程中的多个线程会共享资源,所以在多线程时,可能会共享了不该共享的一些资源。会造成一些安全性的问题;
在一个进程中,如果出现一些线程异常问题。例如野指针等导致线程崩溃,整个进程也会跟着崩溃;
线程的创建
POSIX线程库
库里包含了与线程有关的函数,要使用这些函数库,需要通过引入头文件<pthread.h>
在链接这些线程函数时,在编译文件时需要执行程序要链接的库 “-lpthread”
int pthread_create(pthread_t *thread pthread_attr_t *attr,void*(*start_routine)(void*),void *arg)
// thread 返回线程的id
// attr 设置现成的属性。 可以为NULL (表示使用默认属性)
// start_routine 函数地址 (线程启动后要执行的函数)
// arg 传给线程函数的参数
// 创建线程成功返回0 失败返回错误码。
我们可以通过返回值判定函数出现的错误。同时,库中也给我们提供了线程内errno变量。但是我们通过读取返回值的开销会比读取线程内的errno变量的开销更小。所以一般我们通过返回值判定出现的错误就可以。
pthread_create 会产生一个线程地址, 存放在第一个参数指向的地址中, 这个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程地址。 注意。这个id为线程库的其他后续操作提供了对象。
这里我们要区别之前用于表示一个线程(进程)的id数值。这两个值是不同的。
我们可以通过使用函数
pthread_t pthread_self(void);
来获取本线程的id。
线程退出
- 最直接的方法即该线程的任务结束。函数执行完毕。return结束。
- 通过调用函数
pthread_exit()
自己退出 - 在其他线程中可以通过调用
pthread_cancel
来结束同进程下的其他进程。
void pthread_exit(void* value_ptr);
int pthread_cancel(pthread_t thread);
// thread 即指定需要结束的线程id;
//成功返回0, 失败返回错误码
线程等待
int pthread_join(pthread_t thread, void **value_ptr);
// thread 即使目标线程id
// 一个指向指针的指针, 两次解引用后是线程的返回值。
//成功返回0 失败返回错误码
调用该函数的线程将挂起 直到目标线程终止。 目标线程以不同的方法终止,会有不同的终止状态。
- 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
- 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数
PTHREAD_ CANCELED
- 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
- 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。