[toc]
进程/线程/协程
背景
单任务处理:早期的计算机是什么样的, 卡带机? 真空管?
多任务处理系统: 多线程, 多进程
进程和线程的区别
进程有独立的内存空间, 操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。
线程依赖于进程, 共享内存空间. 只拥有少量的私有内存空间,如程序计数器,栈.
Linux创建进程采用fork()和exec()
fork: 采用复制当前进程的方式来创建子进程,此时子进程与父进程的区别仅在于pid, ppid以及资源统计量(比如挂起的信号)
exec:读取可执行文件并载入地址空间执行;一般称之为exec函数族,有一系列exec开头的函数,比如execl, execve等
fork过程复制资源包括代码段,数据段,堆,栈。fork调用者所在进程便是父进程,新创建的进程便是子进程;在fork调用结束,从内核返回两次,一次继续执行父进程,一次进入执行子进程。
Linux用户级线程创建:通过pthread库中的pthread_create()创建线程
Linux内核线程创建: 通过kthread_create()
线程的管理
在Linux内核中,线程是以轻量级进程的形式存在的,拥有独立的进程表项;而所有的线程创建、同步、删除等操作都在核外pthread库中进行。这种模式称为基于核心轻量级进程的"一对一"线程模型,也就是一个线程实体对应一个核心轻量级进程,线程之间的管理在核外函数库中实现。内核为每一个进程构造了一个管理线程,负责处理线程相关的管理工作,这样做的好处就是线程的调度由核心完成了,而其他诸如线程取消、线程间的同步等工作,都是在核外线程库中完成的。
-
协程
进程和线程都是操作系统定义的概念, 现在的软件开发语言也大多采用了操作系统的实现, 即: 在java里new Thread(), 实质是调用操作系统的pthred.
协程操作系统并没有定义, 而且协程的概念出现很久, 但是并不清晰. 到底什么样的实现满足协程的概念, 并没有一个权威的定义, 目前能看到两种实现方式, 1是保存栈状态 2是不保存栈状态(参见javascript的多异步处理,回调,promise)协程解决了什么问题:
线程说: 进程太重了, 每次创建要申请内存, 定义进程结构...., 所以有了线程的概念, 共享进程内存空间, 初始化需要做的工作很少.
协程说: 线程太重了, 还是需要操作系统调用, 且令人讨厌的进程切换, 想像一下你正在行云流水般的处理你的程序逻辑, 来一个ding, 你思路突然被打断, 要先去处理另一个问题....
协程普遍上调度不在交由操作系统完成, 而是开发人员定义了代码的yield点,
协程有多古老
1960年夏天,D. E. Knuth就是利用开车横穿美国去加州理工读研究生的时间,对着Burroughs 205机器指令集手写COBOL编译器。最早提出“协程”概念的Melvin Conway,也是从如何写一个只扫描一遍程序(one-pass)的COBOL编译器出发。
协程的实现
不信来看看javascript发展史
javascript是单线程运行的, 来看一下javascript异步多任务的处理方式
promise的链式调用
// 例2
Promise.resolve(1)
.then(x => x + 1)
.then(x => {
throw new Error('My Error')
})
.catch(() => 1)
.then(x => x + 1)
.then(x => console.log(x)) //2
.catch(console.error)
生成器Generators/ yield
function *foo(x) {
let y = 2 * (yield (x + 1))
let z = yield (y / 3)
return (x + y + z)
}
let it = foo(5)
console.log(it.next()) // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}
nodeJs 有npm install co ,co库, 专门处理上述Generator. 但是原理不会变.
Generator 函数调用和普通函数不同,它会返回一个迭代器(funciton foo)
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
怎么用
function* loadUI() {
showLoadingScreen();
yield loadUIDataAsynchronously();
hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()
// 卸载UI
loader.next()
async/await
async function async1() {
return "1"
}
console.log(async1()) // -> Promise {<resolved>: "1"}
对着javascript看java, 有种似曾相似的感觉
@Generator
public class HelloWorld {
@GeneratorMethod
public Iterable<Integer> it() {
yield(1);
yield(2);
yield(3);
yield(4);
yield(5);
yield(6);
return null;
}
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
for (Integer message : hello.it()) {
System.out.println(message);
}
System.out.println("end");
}
}
Quasar
new Fiber<V>() {
@Override
protected V run() throws SuspendExecution, InterruptedException {
// your code
}
}.start();
new Fiber<Void>(new SuspendableRunnable() {
public void run() throws SuspendExecution, InterruptedException {
// your code
}
}).start();
FiberScheduler是Quasar框架中核心的任务调度器,负责管理任务的工作者线程WorkerThread,之前提到的他是一个FiberForkJoinScheduler。
ForkJoinPool的默认初始化个数为Runtime.getRuntime().availableProcessors()。
网友评论