如何快速入门VUE3.0:进入学习
多个上下文访问SharedArrayBuffer时,如果同时对缓冲区执行操作,就可能出现资源争用问题。Atomics API 通过强制同一时刻只能对缓冲区执行一个操作,可以让多个上下文安全地读写一个SharedArrayBuffer。
原子操作的本质会排斥操作系统或计算机硬件通常会自动执行的优化(比如指令重新排序)。原子操作也让并发访问内存变得不可能,如果应用不当就可能导致程序执行变慢,为此,Atomics API 的设计初衷是在最少但很稳定的原子行为基础上,构建复杂的多线程JavaScript程序。
Atomics API 提供了一套简单的方法用于执行就地修改操作。在ECMA规范中,这些方法被定义为AtomicReadModifyWrite操作。在底层,这些方法都会从SharedArrayBuffer中某个位置读取值,然后执行算术和位操作,最后再把计算结果写到相同的位置。这些操作的原子本质意味着上述读取、修改、写回操作会按顺序执行,不会被其它线程中断。
//创建大小为1的缓冲区let sharedArrayBuffer = new SharedArrayBuffer(1); //基于缓冲创建Unit8Arraylet typedArray = new Unit8Array(sharedArrayBuffer); //所有ArrayBuffer全部初始化为0console.log(typedArray); //Unit8Array[0] //对索引0处的值执行原子加10Atomics.add(typedArray,0,10); //Unit8Array[10] //对索引0处的值执行原子减10Atomics.sub(typedArray,0,10); //Unit8Array[0]
浏览器的JavaScript编译器和CPU架构本身都有权限重排指令以提升程序执行效率。正常情况下,JavaScript的单线程环境是可以随时进行这种优化的,但是,多线程中的指令重排可能导致资源争用,而且极难排错。
Atomics API 通过两种主要方式解决这个问题:
所有原子指令相互之间的顺序永远不会重排。
使用原子读或原子写保证所有指令都不会相对原子读写重新排序。
除了读写缓冲区的值,Atomics.load()和Atomics.store()还可以构建“代码围栏”。JavaScript引擎保证非原子指令可以相对于load()和store()本地重排,但这个重排不会侵犯原子读写的边界。
const sharedArrayBuffer = new SharedArrayBuffer(4); const view = new Unit32Array(sharedArrayBuffer); //执行非原子写view[0] = 1; //非原子写可以保证在这个读操作之前完成,因此这里一定会读到1console.log(Atomics.load(view,0)); //1 //执行原子写Atomics.store(view,0,2); //非原子读可以保证在原子写完成后发生,这里一定会读到2console.log(view[0]); //2
为了保证连续、不间断的先读后写,Atomics API 提供了两种方法:exchange()和compareExchange()。Atomics.exchange()执行简单的交换,以保证其他线程不会中断值得交换。
const sharedArrayBuffer = new SharedArrayBuffer(4); const view = new Unit32Array(sharedArrayBuffer); //在索引0处写入10Atomics.store(view,0,10); //从索引0处读取值,然后在索引0处写入5console.log(Atomics.exchange(view,0,5)); //10 //从索引0处读取值console.log(Atomics.load(view,0)); //5
在多线程程序中,一个线程可能只希望在上次读取某个值之后没有其他线程修改该值得情况下对共享缓冲区执行写操作。如果这个值没有被修改,这个线程就可以安全地写入更新后的值:如果这个值被修改了,那么执行写操作将会破坏其他线程计算的值。对于这种任务,Atomics API提供了compare-Exchange()方法。这个方法只在目标索引处的值与预期值匹配时才会执行写操作。
如果没有某种锁机制,多线程程序就无法支持复杂需求。为此,Atomics API提供了模仿Linux Futex(快速用户空间互斥量,fast user-space mutex)的方法。这些方法本身虽然非常简单,但可以作为更复杂锁机制的基本组件。
所有原子Futex操作只能用于Int32Array视图,而且,只能用在工作线程内部。
跨文档消息,有时候也称XDM(cross-document messaging),是一种在不同执行上下文(例如不同工作线程或不同源的页面)间传递信息的能力。
Encoding API主要用于实现字符串与定型数组之间的转换。
File API仍然以表单中的文件输入字段为基础,但是增加了直接访问文件信息的能力。HTML5在DOM上为文件输入元素增加了files集合。当用户在文件字段中选择一个或多个文件时,这个files集合中会包含一组File对象,表示被选中的文件,每个File对象都有一些只读属性。
FileReader类型表示一种异步文件读取机制,可以把FileReader想象成类似于XMLHttpRequest,只不过用于从文件系统读取文件,而不是从服务器读取数据。FileReader类型提供了几个读取文件数据的方法。
readAsText(file,encoding);//从文件中读取纯文本内容并保存在result属性中
readAsDataURL(file);//读取文件并将内容的数据URI保存在result属性中
readAsBinaryString(file);//读取文件并将每个字符的二进制数据保存在result属性中
readAsArrayBuffer(file);//读取文件并将文件内容以ArrayBuffer形式保存在result属性中
FileReader类型的同步版本。
某些情况下,可能需要读取部分文件而不是整个文件,为此,File对象提供了一个名为slice()的方法。slice()方法接收两个参数:起始字节和尧都区的字节数。这个方法返回一个Blob的实例,而Blob实际上是File的超类。
blob表示二进制大对象,是JavaScript对不可修改二进制数据的封装类型。包含字符串的数组、ArrayBuffers、ArrayBufferViews,甚至其他Blob都可以用来创建blob。Blob构造函数可以接收一个options参数,并在其中指定MIME类型。
Streams API 是为了解决一个简单但又很基础的问题而生的:Web应用如何消费有序的小信息块而不是大块信息?这种能力主要有两种应用场景。
Streams API定义了三种流:
可读流:可以通过某个公共接口读取数据块的流。数据在内部从底层源进入流,然后由消费者consumer进行处理。
可写流:可以通过某个公共接口写入数据块的流。生产者(consumer)将数据写入流,数据在内部传入底层数据槽(sink)。
转换流:由两种流组成,可写流用于接收数据,可读流用于输出数据。这两个流质检是转换程序(transformer),可以根据需要检查和修改流内容。
Web Cryptography API描述了一套密码学工具,规范了JavaScript如何以安全和符合惯例的方式实现加密。这些工具包括生成、使用和应用加密秘钥对,加密和解密信息,以及可靠地生成随机数。
在需要生成随机数时,很多人会使用Math.random()
。这个方法在浏览器中是以伪随机数生成器(PRNG,PseudoRandom Number Generator)方式实现的。所谓的伪指的是生成值的过程不是真的随机。PRNG生成的值只是模拟了随机的特性。浏览器的PRNG并未使用真正的随机源,只是对一个内部状态应用了固定的算法。每次调用Math.random()
,这个内部状态都会被一个算法修改,而结果会被转换为一个新的随机数。例如,V8引擎使用了一个名为xorshift128+
的算法来执行这种修改。
由于算法本身是固定的,其输入只是之前的状态,因此随机数顺序也是确定的。xorshift128+
使用128位内部状态,而算法的设计让任何初始状态在重复自身之前都会产生2128-1个伪随机值。这种循环被称为置换循环,而这个循环的长度被称为一个周期。很明显,如果攻击者知道PRNG的内部状态,就可以预测后续生成的伪随机值。如果开发者无意中使用了PRNG生成了私有密钥用于加密,则攻击者就可以利用PRNG的这个特性算出私有密钥。
伪随机数生成器主要用于快速计算出看起来随机的数,不过并不适合用于加密算法,为解决这个问题,密码学安全伪随机数生成器(CSPRNG,Cryptographically Secure PseudoRandom Number Generator),额外增加了一个熵作为输入,例如测试硬件时间或其它无法预计行为的系统特性,虽然速度上不及PRNG,但是生成的值更难预测了,就可以用于加密。
Web Cryptography API引入了CSPRNG,这个CSPRNG可以通过crypto.getRandomValues()
在全局Crypto
对象上访问。与Math.random()
返回一个0到1之间的浮点数不同,getRandomValues()
会把随机值写入作为参数传给它的定型数组。定型数组的类不重要,因为底层缓冲区会被随机的二进制位填充。