CLR Via C# 3rd 阅读摘要 -- Chapter 28 – Primitive Thread Synchronization Constructs

Class Libraries and Thread Safety

  1. 线程同步是用来避免多个线程同时访问共享数据时出现冲突;
  2. 线程同步的障碍:
    • 1.极其乏味易错;
    • 2.锁严重影响性能;
    • 3.线程同步锁在同一时间点仅允许一个线程访问资源。
  3. 设计程序时应该尽可能的避免线程同步,最好避免采用static字段的共享数据;
  4. 尝试使用值类型,因为它们总是拷贝传递的,因此每个线程在自身拥有的拷贝上操作。所以当多个线程同时以只读方式访问值类型共享数据时是安全的。
  5. FCL保证所有的静态方法是线程安全的;
  6. 当一个线程构造一个对象,只有该线程拥有该对象的引用,其他线程不能访问该对象;
  7. 类型设计时应遵循的模式:确保所有的静态方法多线程安全、所有的实例方法多线程不安全;
    一个例外:如果实例方法是用于协调多线程的,那么该实例方法也应该多线程安全。如CancellationTokenSource.Cancel方法。

Primitive User-Mode and Kernel-Mode Constructs

  1. 两种原生线程同步结构:
    • 用户模式:速度很快,使用特定的CPU指令协调线程,协调工作由硬件完成。Windows系统不会检测线程是否阻塞在用户模式同步结构;
      线程池线程阻塞在用户模式同步结构不会被当成阻塞,线程池不会创建新的线程来代替临时阻塞的线程。
      采用该模式的线程会被系统抢占调度,可能导致线程被反复快速调度,从而会浪费CPU;
    • 核心模式:Windows操作系统提供,调用实现在系统内核的函数。当一个线程使用内核模式同步结构来请求其他线程持有的资源,Windows将阻塞该线程所以不会浪费CPU;
      线程在用户模式与核心模式间互相转换会严重损害性能;
    • 如果一个线程持有一个同步结构不再释放它,等待该结构的线程将永远被阻塞:
      • 活锁:如果该同步结构是用户模式,线程一直运行在CPU上;
      • 死锁:如果该同步结构是核心模式,线程被阻塞住;
    • 活锁与死锁都很糟糕,但相比之下,活锁更糟糕,因为活锁既浪费CPU又浪费内存,而死锁只浪费内存;

User-Mode Constructs

  1. 原生的用户模式结构:
    • 易变(volatile)结构:在一个简单数据类型变量上执行原子读或写操作;
    • 联锁(Interlocked)结构:在一个简单数据类型变量上执行原子读和写操作;
  2. 易变结构确认读或写操作是否原子的非常重要,它们还控制这些原子操作的时机。联锁结构执行操作要比简单的读和写操作复杂一些,它们也需要控制操作的时机;
  3. 比如一个Int64的变量如果没有正确对齐,那么当多线程读写该变量时,可能会出现只正确读取前四字节或后四字节,这就是脏读(torn read)?
  4. System.Threading.Thread.VolatileWrite(), .VolatileRead(), .MemoryBarrier()静态方法通常禁止C#编译器、JIT编译器、CPU进行优化;
  5. 当线程通过共享数据进行通信时,对最后一个值的写操作调用VolatileWrite(),对第一个值的读操作调用VolatileRead();
  6. C# volatile关键字:JIT编译器会对标记为volatile的字段的读写进行VolatileWrite()和VolatileRead()处理;
  7. 将来,C#不支持通过引用传递volatile字段到方法调用中;
  8. 现在,Interlocked.Exchange(), .CompareExchange()只支持简单值类型,将来还会提供对Object, IntPtr, Single, Double以及泛型版本的支持;
  9. SimpleSpinLock的缺陷,当锁出现竞争时,线程会不停轮转,浪费CPU资源:
    internal struct SimpleSpinLock {
        private Int32 m_ResourceInUse;  //0 = false (default), 1 = true
        
        public void Enter() {
            // Set the resource to in-use and if this thread
            // changed it from Free, then return
            while (Interlocked.Exchange(ref m_ResourceInUse, 1) != 0) {
                // Black Magic goes here...
            }
        }
        
        public void Leave() {
            // Mark the resource as Free
            Thread.VolatileWrite(ref m_ResourceInUse, 0);
        }
    }
  10. FCL提供的System.Threading.SpinWait
  11. Thread.Sleep(),允许线程资源放弃时间片,休眠时间是近似值;
  12. Thread.Yield(),告诉Windows在当前CPU上调度其他线程。如果当前CPU上有其他线程,返回true并结束调用线程的时间片,被调度线程结束自身的时间片之后,调用线程继续。
  13. Thead.SpinWait(),线程强制自己暂停,允许超线程CPU切换到其他线程。该方法会使用特别的CPU指令,如果CPU不支持超线程,该指令被忽略;
  14. Win32 API:Sleep(), SwitchToThread(), YieldProcessor();
  15. 当集合中的每一项都需要关联锁的时候,轻量级、内存友好的SpinLock是个好东西。但是要注意不要传递它的实例,因为是值类型,传递的是拷贝;

Kernel-Mode Constructs

  1. 核心模式的同步结构比用户模式的要慢许多,因为它们由Windows操作系统协调,此外每个核心对象上的方法调用都会导致调用线程从托管代码转换到本地用户模式代码再到本地核心模式代码,然后再原路返回;
  2. 核心模式同步结构提供哪些用户模式所没有的优点:
    • 当核心模式同步结构检测到资源上出现竞争时,Windows阻塞竞争失败的线程,因此不再浪费处理器资源;
    • 核心模式同步结构可以同步本地线程和托管线程;
    • 核心模式同步结构可以同步运行在同一台机器上不同处理器上的线程;
    • 核心模式同步结构可以附上安全限制避免未经认证的帐号访问他们;
    • 线程能够被阻塞直到所有的核心模式同步结构都可用,或者任何一个可用;
    • 阻塞在核心模式同步结构上的线程可以设置一个timeout值;如果这段时间内没能获得期望的资源,那么不再阻塞以干点其他正事。
  3. 原生的核心模式结构:
    • 事件(Events):事件是有内核管理的Boolean变量。当事件为false时,线程被阻塞。有两种事件:AutoResetEventManualResetEvent
    • 信号量(Semaphore):信号量是内核管理的Int32变量。当信号量为0时,线程被阻塞;>0时,线程不被阻塞。
  4. 核心模式结构的层次结构:
      WaitHandle
      |--EventWaitHandle
      |--|--AutoResetEvent
      |--|--ManualResetEvent
      |--Semaphore
      |--Mutex
  5. 每个在核心模式同步结构上的方法调用都会出现完全内存保护;
  6. SimpleWaitLock vs SimpleSpinLock
    当锁上没有竞争时,SimpleWaitLockSimpleSpinLock要慢许多,因为SimpleWaitLockEnterLeave方法会强制调用线程在托管代码到核心代码之间进行来回转换;
    但是当锁上有竞争时,SimpleWaitLock不会浪费CPU资源,而SimpleSpinLock会不停的轮转CPU;
  7. AutoResetEventManualResetEventSemaphore行为比较:
    • 当多个线程在AutoResetEvent上等待时,设置事件只会让一个线程不再被阻塞;
    • 当多个线程在ManualResetEvent上等待时,设置事件会让所有线程不再被阻塞;
    • 当多个线程在Semaphore上等待时,释放信号量会让releaseCount个线程不再被阻塞(relaseCountSemaphore.Release()方法的参数)。
  8. 互斥体(Mutex)表现为一个同斥锁;
  9. Mutex很像AutoResetEvent或者Semaphore(count = 1),但有一些更复杂的逻辑:
    • 首先,Mutex对象通过查询调用线程的ID记录了哪个线程获得了它。当线程调用ReleaseMutexMutex会确认是否为同一线程;
    • 其次,Mutex维护一个递归计数指出被获得了多少次。只有当递归计数降为0时,其他线程才能获取。
  10. Mutex对象需要更多的内存,并且需要维护额外信息,所以会比较慢;
  11. 如何当一个单独的核心结构可用时调用方法?使用System.Threading.ThreadPool.RegisterWaitForSingleObject静态方法。

本章小结

    本章讲述了原生的线程同步结构,首先介绍了类库和线程安全性概念,然后对线程同步模式进行了分类:用户模式与核心模式,接着详细说明了这两种同步模式的实现细节,并举例进行了对比。

转载于:https://www.cnblogs.com/bengxia/archive/2010/07/01/1768923.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值