• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2021-07-15 14:29 Aet 隐藏边栏 |   抢沙发  3 
文章评分 1 次,平均分 5.0

概述

  1. 当所有线程都能独自运行而不需要相互通信的时候,Microsoft Windows将进程最佳运行状态。
  2. 系统中的所有线程必须访问系统资源,比如堆、串口、文件、窗口以及无数其他资源。
    如果一个线程独占了对某个资源的访问,那么其他线程就无法完成它们的工作。
  3. 以下两种基本情况下,线程间需要相互通信:
    1. 需要让多个线程同时访问一个共享资源,同时不能破坏资源的完整性。
    2. 一个线程需要通知其他线程某项任务已完成。

原子访问:Interlocked系列函数

  1. 关于这些Interlocked函数如何工作的,取决于代码运行的CPU平台。
    如果是X86系列CPU,那么Interlocked函数会在总线上维持一个硬件信号,这个信号会阻止其他CPU访问同一个内存地址。
  2. 我们必须确保传给这个函数的变量地址是经过对齐的,否则这些函数可能会失败。
    1. C运行库提供了一个_aligned_malloc函数,这个函数可以用来分配一块对齐过的内存。

  1. 对于32位应用程序而言,InterlockedExchange和InterlockedExchangePointer是以一个32的值替换了32的值。
    对64位应用程序而言,InterlockedExchange替换的是一个32位的值,而InterlockedExchangePointer替换的是64位的值。
    这两个函数都会返会原来的值。
  2. 此外,必须保证变量和锁所保护的数据位于不同的高速缓存行中。
    如果锁变量和数据共享同一高速缓存行,那么使用资源的CPU就会与任何试图访问该资源的CPU发生争夺,从而影响到性能。

  1. 下面这个函数以原子的方式执行一个测试和设置操作。
    对32位应用程序而言,这两个函数都是对32位的值进行操作。
    对64位应用程序而言,InterlockedCompareExchange对32位值进行操作,而InterlockedCompareExchangePointer对64位值进行操作。

  1. 从Windows XP开始,除了能对整数或布尔值进行这些原子操作外,还能用一系列其他的函数对一种被称为Interlocked单向链表的栈进行操作。
    栈中的每个操作,如入栈或出栈,必定是以原子的方式进行的。
函数 描述
InitializeSListHead 创建一个空栈
InterlockedPushEntrySList 在栈顶添加一个元素
InterlockedPopEntrySList 移除位于栈顶的元素并将它返回
InterlockedFlushSList 清空栈
QueryDepthSList 返回栈中元素的数量

高速缓存行

  1. 当CPU从内存中读取一个字节的时候,它并不只是从内存中取回一个字节,而是取回一个高速缓存行。
  2. 高速缓存行可能包含32个字节(老式CPU),64个字节,甚至是128个字节(具体取决于CPU),它们始终都对齐到32字节边界,64字节边界或128字节边界。
  3. 高速缓存行的存在的目的是为了提高性能。
    一般来说,应用程序会对一组相邻的字节进行操作。如果所有字节都在高速缓存中,那CPU就不必访问内存总线,因为后者耗费的时间要多得多。
  4. 当一个CPU修改了高速缓存行中的一个字节时,机器中的其他CPU会收到通知,并使自己的高速缓存行作废。
  5. 意味着应该按照高速缓存行的大小来将应用程序的数据组织在一起,并将数据与缓存行的边界对齐。
    目的是为了确保不同的CPU能够各自访问不同的内存地址,而且,这些内存地址不再同一个高速缓存行中。
    此外,还应该把只读的数据(或者不经常读的数据),与可读可写的数据区分开来存放。
    还应该把差不多会同一段时间访问的数据组织在一起。

缓存行的大小

  1. 调用Win32的GetLogicalProcessorInformation函数,可以返回一个SYSTEM_LOGICAL_PROCESSOR_INFORMATION的结构体数组。
  2. 在这个结构体数组里面,每个结构的Cache字段,是一个CACHE_DESCRIPTOR结构,这个结构里面的LineSize就表示的是CPU的高速缓存行的大小。
  3. 根据这个缓存行的大小,我们可以使用C/C++编译器的__declspec(align(#))指示符来对字段加以控制。

关于高级线程同步

  1. 当线程想要访问一个共享资源或想要得到一些“特殊”事件的通知时,线程必须调用操作系统的一个函数,并将线程正在等待的东西作为参数传入。
  2. 如果操作系统检测到,资源已经可供使用了,或者特殊事件已经发生了,那么这个函数会立即返回,这样线程将仍然保持可调度状态。
  3. 如果无法取得对资源的访问权,或者特殊事件尚未发生,那么系统会将线程切换到等待状态,使线程变得不可调度,从而避免了让线程浪费CPU时间。
  4. 当线程在等待的时候,系统会充当它的代理。系统会记住线程想要访问什么资源,当资源可供使用的时候,系统会自动将线程唤醒(线程的执行与特殊事件是同步的)。

volatile

  1. 这个限定符高速编译器不要对这个变量进行任何形式的优化,而是始终从变量在内存中的位置读取变量的值。
  2. 给一个结构体加volatile限定符,等于给结构中的每一个成员都加上volatile限定符。可以确保任何一个成员始终都是从内存中读取的。

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0 最后编辑于:2021-11-20
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享