线程引入原因
-
性能
操作系统开销大;
Unix的轻型进程(fork) -
应用
进程代码并发执行的需求;
例子:PPT编辑(输入/拼写检查/存盘) -
硬件
多核处理器;
加速进程的运行
线程
线程
- 可在CPU上运行的基本执行单位
- 进程内的一个代码片段可以被创建称为一个线程
- 线程状态:就绪、运行、等待等
- 线程操作:创建、撤销、等待、唤醒等
- 进程依旧是资源分配的基本单位
- 线程自己不拥有系统资源,通过进程申请资源
传统进程
- 重型进程
- 只有一个线程:主线程
- 单线程模型
进程和线程
方面 | 进程 | 线程 |
---|---|---|
代码 | 进程包含线程 | 线程是进程中的一段代码 |
资源 | 进程是资源分配基本单位 | 线程不拥有资源,共享使用进程的资源 |
调度 | 同一进程中的线程切换不会引起进程切换 | 线程是基本调度单位 |
切换 | 重量级上下文切换,代价大 | 轻量级切换,代价小 |
生命期 | 进程撤销会导致它的所有线程被撤销 | 线程撤销不会影响进程 |
线程结构
- 代码和数据:来自进程
- 各类资源:来自进程
- 线程控制块(TCB)
包含线程ID,程序计数器PC,寄存器集,栈空间
Windows线程:
单线程和多线程
单线程:
一个进程只有一个线程;
早期操作系统
多线程:
一个进程有多个线程;
支持线程的操作系统
线程优点
- 响应度高
线程创建开销小;
例子:web服务器 - 资源共享
进程中的线程可以共享进程资源 - 经济性
线程创建、上下文切换比进程快;
Solaris:创建线程比进程快30倍,线程切换比进程切换快5倍 - MP体系结构的运用
一个进程中的线程在不同处理器上并行运行
Web服务器
Windows线程
- 操作系统支持线程
- 线程主要数据结构:
(1)执行线程块ETHREAD
Executive thread block
对应程序地址
指向KTHREAD指针
内核空间
(2)核心线程块KTHREAD
Kernel thread block
线程调度和同步信息
内核空间
(3)线程环境块TEB
Thread environment block
用户空间的数据结构,线程本地存储
Linux 线程
- Linux 2.2版引入线程
- 通过clone() 系统调用创建线程
类似fork
Linux只支持克隆线程 - 用术语“任务”表示进程和线程,一般不用“线程”
- 用户线程:PThreads
Clone
- 创建进程或者线程
- Clone可有选择继承父进程的资源
不和父进程共享
int clone(int (\*fn)(void *),void *child_stack, int flags, void *arg)
fn:函数指针,指向一个函数体,即想要创建进程的静态程序
child_stack:给子进程分配系统堆栈的指针
flags:要复制资源的标志,描述从父进程继承的资源
多线程模型
用户(管理)线程
用户线程:由用户线程库进行管理的线程
- 内核看不到用户线程
- 用户线程的创建和调度在用户空间内,不需要内核的干预
- 应用于传统的只支持进程的操作系统
例子:
- POSIX Pthreads
- Win32 threads
- Java threads
内核(管理)线程
内核线程:内核进行管理的线程
- 需要内核支持
- 由内核完成线程调度
- 由内核进行创建和撤销
例子:Windows XP/2000,Solaris,Linux,Tru64 UNIX,Mac OS X
多线程模型
多对一模型
不支持内核线程的操作系统
- 内核只有进程
内核只看到一个进程
- 多个线程不能并行运行在多个处理器上
进程中的用户线程由进程自己管理
- 进程内线程切换不会导致进程切换
- 一个线程的系统调用会导致整个进程阻塞
例子:
- Solaris Green Threads
- GNU Portable Threads
一对一模型
用于支持线程的操作系统
- 用户线程映射到内核线程
- 操作系统管理这些线程
并发性好:多个线程可并行运行在多个处理器上
内核开销大
例子:
Windows
OS/2
多对多模型
多个用户线程映射为相等或更小数目的内核线程
- 并发性和效率兼顾
- 增加复杂度
例子:Solaris 9 以前的版本
Solaris 2 多对多模型
两级模型
类似于M:M
例子:IRIX,HP-UX,Tru64 UNIX,Solaris 8及早期版本
线程库
线程库
为程序员提供API来创建和管理线程
两种模式:
(1)用户库(用户线程)
- 存在于用户空间
- 没有内核支持
- 调用线程库不会产生系统调用
(2)内核库(内核线程)
- 存在于内核
- 操作系统支持
- 调用线程库会产生系统调用
Pthreads线程库
Pthreads:POSIX线程(POSIX threads)
- 线程的POSIX标志
- 定义了创建和操纵线程的一整套API
- 用在类Unix操作系统(Unix、Linux、Mac OS X等)
- Windows也有移植版pthreads-win32
- 一般多为用户线程
POSIX标准:可移植操作系统接口(Portable Operating System Interface)
- 定义了操作系统为应用程序提供的接口标准
- 为各种UNIX软件定义的一系列API标准总称
常用线程操作
- pthread_create():创建一个线程
- pthread_exit():终止当前线程
- pthread_cancel():中断另外一个线程的运行
- pthread_join():阻塞当前的进程,直到另外一个线程运行结束
- pthread_attr_init():初始化线程的属性
- pthread_t:线程ID
- pthread_attr_t:线程属性
Pthreads例子
gcc -o mypthread -lpthread mypthread.c
#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
pthread_t ntid;
void *thr_fn(void *arg){
printids("new thread:");
return((void*)0);
}
int main(){
int err;
err=pthread_create(&ntid,NULL,thr_fn,NULL);
if(err!=0){
printf("can't create thread:%s\n",strerror(err));
return 1;
}
sleep(1);
return 0;
}
Java线程库
Java线程由JAVA虚拟机JVM管理
- Java线程操作系统不可见
- 用户线程
- 定义了创建和操纵线程的一整套API
- 跨操作系统平台
Java线程创建
- 扩展java.lang,Thread类(书本例子)
- 实现Runnable接口
Java线程例子
public class DoSomething implements Runnable{//Runnable接口
private DoSomething(String name){
this.name=name;
}
public void run(){//Run方法
System.out.println(name+":"+i);
}
}
public class TestRunnable{
public static void main(String[] args){
DoSomething ds1=new DoSomething("1");
DoSomething ds2=new DoSomething("2");
Thread t1 =new Thread(ds1);
Thread t2=new Thread(ds2);
t1. start();
t2. start();
}
}
java线程状态
win32线程库
- win32线程库是内核库
- 内核线程
- 线程创建方法:Win32 API,MFC,.Net Framework
- 线程创建:
HANDLE CreateThread(LPSECURITY_ATTRIBUTES IpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE IpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD IpThreadld);
线程函数原型:DWORD WINAPI threadfunc(LPVOID param); - 线程挂起:
DWORD SuspendThread(HANDLE hThread); - 线程恢复:
DWORD ResumeThread(HANDLE hThread); - 线程退出:
VOID ExitThread(DWORD dwExitCode);
BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);
win32线程例子
#include"stdafx.h"
#include <windows.h>
#include <iostream>
DWORD WINAPI Func(LPVOID param){
while(true){
Sleep(1000);
cout<<"hello!";
}
return 0;
}
int main(int argc, char* argvI){
int input=0;
HANDLE hand1=Create Thread(NULL,0, Func,(void*)& input, CREATE_SUSPENDED, NULL);
while(true){
cin>>input;
if(input==1){
Resume Thread(hand1);
}else if(input==2){
Suspend Thread(hand1);
}else{
Terminate Thread(hand1,1);
return 0;
}
}
return 0;
}