前言
进程:一个正在运行的程序。
线程:进程内部的一条执行路径…
目录
1.线程的概念与实现方式
线程的概念
(1)线程是进程内部的一条执行序列或执行路径,即一个可调度的实体。一个进程可以包含多条线程。示例:
(2)线程可分为内核线程和用户线程。
- 内核线程,运行在内核空间,由内核来调度。
- 用户线程,运行在用户空间,由线程库来调度。
线程的实现方式
线程的实现方式可分为三种模式:
- 内核级线程(完全由内核调度)。
- 用户级线程(完全在用户空间实现)。
- 组合级线程(双层调度)。
Linux中线程的实现
Linux 实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux 把所有的线程都当做进程来实现。 内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。 每个线程都拥有唯一隶属于自己的 task_struct(统称为PCB(process control block)程序控制块),所以在内核中,它看起来就像是一个普通的进程(只是线程和其他一些进程共享某些资源,如地址空间)。
不同实现方式的特点
-
用户级线程:无需内核的支持,内核甚至不知道这些线程的存在。线程库负责管理这些执行线程。线程库利用 longjmp 来控制这些线程的执行,使它们看起来是“并发”运行的。
特点:由于不占用额外的内核资源,所以即使一个进程创建了很多个线程,也不会对系统性能造成很大影响。
其缺点是﹔对于多处理器系统,一个进程的多个线程无法运行在不同的CPU上,因为内核是按照其最小调度单位来分配CPU的。此外,线程的优先级只对同一个进程中的线程有效,比较不同进程中的线程的优先级没有意义。 -
内核级线程 :将创建、调度线程的任务都交给了内核,线程库无需管理。优缺点与用户级线程模式相反。
-
组合级线程 :不但不会消耗过多的内核资源, 而且线程切换速度也较快,同时它可以充分利用多处理器的优势。
进程与线程的区别
- 进程是资源分配的最小单位,线程是CPU调度的最小单位。
- 进程有自己独特的地址空间,线程共享进程中的地址空间。
- 进程的创建消耗资源大,线程的创建相对较小。
- 进程的切换开销大,线程的切换相对较小。
2.线程的使用
线程库的接口介绍
现代Linux默认使用的线程库是 NPTL。
输入以下命令查看:
getconf GNU_LIBPTHREAD_VERSION
创建和结束线程的基础API,都包含在头文件
#include<pthread.h>
1.创建线程
pthread_create()
定义如下:
NAME
pthread_create - create a new thread
SYNOPSIS
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
参数介绍:
- thread: 接收创建的线程的 ID
- attr: 指定线程的属性
- start_routine: 指定线程函数
- arg: 给线程函数传递的参数
类型 pthread_t的定义为:
typedef unsigned long int pthread_t
pthread_create创建成功时返回0, 失败时返回错误码。
2.退出线程
pthread_exit()
定义如下:
NAME
pthread_exit - terminate calling thread
SYNOPSIS
#include <pthread.h>
void pthread_exit(void *retval);
Compile and link with -pthread.
参数介绍:
- retval: 向线程的回收者传递退出信息。它执行完成后不会返回调用者,而且永远不会失败。
3.等待线程结束
pthread_join()
一个进程中的所有线程都可以调用pthread_join函数来回收其他线程(前提是目标线程是可回收的),即等待其他线程结束 ,这类似于回收进程的wait和 waitpid系统调用。
定义:
NAME
pthread_join - join with a terminated thread
SYNOPSIS
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
参数介绍:
- thread :目标线程的标识符
- retval:目标线程返回的退出信息。
注意:
该函数会一直阻塞,直到被回收的线程结束为止。成功时返回0,失败返回错误码。常见错误码如下:
错误码 | 含义 |
---|---|
EDEADLK | 可能引起死锁, 比如两个线程互相针对对方调用pthread_join(),或者线程自己调用pthread_join |
EINVAL | 目标线程是不可回收的,或者已有其他线程在回收该目标线程 |
ESRCH | 目标线程不存在 |
多线程代码示例
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
void* pthread_fun(void* arg)
{
int i = 0;
for ( ; i < 5; ++i)
{
sleep(1);
printf("fun thread running.\n");
}
pthread_exit("fun over");
}
int main(void)
{
pthread_t tid;
int res = pthread_create(&tid, NULL, pthread_fun, NULL);
assert(res == 0);
int i = 0;
for (; i < 5; ++i)
{
sleep(1);
printf("main pthread running.\n");
}
char* s = NULL;
pthread_join(tid, (void**)&s);
printf("s = %s\n", s);
exit(0);
}
运行结果:
线程并发运行
代码示例:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<pthread.h>
void* thread_fun(void* arg)
{
int index = *((int*)arg);
int i = 0;
for (; i < 5; ++i)
{
printf("index = %d\n", index);
sleep(1);
}
}
int main(void)
{
pthread_t id[5];
int i = 0;
for (; i < 5; ++i)
{
pthread_create(&id[i], NULL, thread_fun, (void*)&i);
}
for (i = 0; i < 5; ++i)
{
pthread_join(id[i], NULL);
}
exit(0);
}
第一次运行结果:
第二次运行结果: