线程同步构造

线程同步总体上分为两种构造:用户模式和内核模式。其中用户模式基于CPU指令,内核模式则基于操作系统内核对象。由于用户模式构造由硬件支持,且不发生用户-内核状态切换,因此效率较高。但线程使用基于用户模式的同步时,操作系统也无法捕捉到该线程上的阻塞。因此该线程在操作系统看来总是处于非阻塞状态,这意味着线程总是参与抢占时间片,导致线程无谓的浪费CPU时间。与之相对的是内核模式构造,线程使用操作系统提供的内核对象进行同步,每当进入同步块时,代码需要切换到内核态运行,导致巨大的性能损失。但优点是操作系统可以检测到等待同步对象的线程并将其阻塞,当同步对象可用时再唤醒线程。如果拥有“锁”的线程一直不释放,基于用户模式构造的等待线程将一直在CPU上无意义的运行,称之为“活锁”。基于内核模式构造的等待线程则一直被操作系统阻塞,称之为“死锁”。总体来说“死锁”优于“活锁”,因为前者不消耗CPU时间。

同步I/O

在Windows系统中,应用程序调用I/O函数发起的操作均发生在用户态,而用户态是无法访问I/O硬件设备的,因此Windows把用户态的I/O操作信息打包发送给内核,此时进入内核态访问对应的I/O设备。I/O设备处理请求的操作时,发起I/O请求的线程将被阻塞,只有当I/O操作完成时,才会重新唤醒线程以返回结果。如果在Socket中使用同步I/O操作,当大量客户端请求连接时,将消耗大量线程,造成性能的急剧下降。

使用Task

使用ThreadPool可以方便的发起异步操作,但却无法知道操作在何时完成,且无法直接获取操作的返回值,为此CLR提供了对线程池的再一次包装,即Task类。Task内部实际上也是使用线程池,只不过封装了一些高级功能。

CLR线程池

虽然CLR通过System.Threading名称空间提供了对Windows线程API的封装,但直接创建和销毁线程严重影响性能。过多的线程不仅浪费内存,而且会造成频繁的CPU上下文切换,更重要的是线程大部分时间都处于无所事事的状态,浪费宝贵的CPU时间。其次多线程非常容易引发滥用,特别是那些没什么多线程基础的程序员,为了所谓的“性能”无脑使用线程,即使在一些商业软件中这种现象也比比皆是。为了减少这种现象,CLR提供了原生的线程池支持。对于每个CLR,线程池线程将被所有运行中的托管程序集共享。

双端队列简介

顾名思义,deque支持在头尾进行插入删除操作。类似于vector和array,deque也是顺序存储结构,没有了解过deque的人在主观上可能会以为deque与vector底层结构类似,实际上为了支持头部插入与删除操作,deque采用分段连续存储的数据结构,因此实现上比vector复杂得多。因为采用分段设计,deque的迭代器效率比vector低。
为了管理片段,STL的deque在内部定义了一个_Map成员,_Map底层实际是一个T**,也可以将_Map看作一个数组,每一个数组元素指向内存中的一段连续的元素存储空间

迭代器概念

STL设计迭代器的初衷就是将容器细节与算法分离,转而用迭代器作为两者的桥梁。迭代器作为一种访问容器的结构,其本质上也是一种对指针的高级包装而已,因此对应于容器的结构特点,迭代器也分为单向迭代器(典型如单链表)、双向迭代器(如双链表)和随机访问迭代器(如vector等顺序存储容器)。

类继承

C++支持三种不同的继承方式:private、protected和public,继承方式只影响派生类外部对基类成员的访问权限,而不影响派生类在内部对基类成员的访问权限。私有继承意味着所有基类成员将被继承为派生类的私有成员;保护继承意味着基类所有public成员被继承为protected,其它成员不受影响;公有继承则保持基类成员的原有访问控制不变。例子:

OOP

CLR内存分配

无论是C#、VB.NET或是Java,这些所谓的高级编程语言实际上都致力于解决C++中常见的内存泄露问题,方法很简单 – 那就是剥夺程序员对于物理内存空间的直接控制权,重新虚拟一层“可控”的内存空间,并由框架(.NET的CLR以及Java的JVM)自行处理内存问题。这样虽然牺牲了一点点性能以及少许灵活性,但大大提高了安全性,更不用说框架封装所带来的便利。





载入天数...载入时分秒...  |  总访问量为
Powered by Github and MarkdownPad 2

--------------------------- 别拉了,我是有底线的>_< ---------------------------