- 两种构造模式
有两种基元构造:用户模式(user-mode)和内核模式( kernel-mode)。应尽量使用基元用户模式构造,它们的速度要显著快于内核模式的构造。为什么用户模式要快于内核模式呢?这是因为它们使用了特殊CPU指令来协调线程。这意味着协调是在硬件中发生的(所以才这么快)。
换句话说用户模式不是没有阻塞而是阻塞的很短,在硬件层面,对于内核系统来说,他不知道发生了阻塞,所有认为永远不会阻塞。而内核模式就是系统帮我们虚拟出的一个构造,它相比用户构造虽然性能不好,会阻塞但是不会占着cpu一直跑。就像我们while(true)时 ,会让线程睡一会,来让其他线程用一下cpu - 这2种构造模式各有优缺点,将他们组合一下,出行了第三种模式,混合构造hybrid construct,混合构造就能很快的调用又能不浪费cpu的算力。
-
window下的用户模式构造
- 易变构造
易变构造就是说对一个数据的操作是非原子性的,数据可能滞留在cpu缓存中,从而造成数据的脏读脏写(根据数据库脏读而来),如何易变构造呢?
将变量用volatile修饰,可以保证变量的原子性
volatile可以修饰Boolean,(S)Byte,(U)Int16,(U)Int32,(U)IntPtr, Single和 Char类型
public volatile int int_a;
public volatile char char_a;
使用volatile请注意,不要滥用volatile,可能会造成编译器放弃优化部分代码而造成性能损失。
当如下情况 可能会造成一定性能损失,编译时选择优化代码,首选32位系统
internal class vol_Test
{
public volatile int int_a;
public volatile char char_a;
public int int_b;
public int int_c;
public void TestStart()
{
for (int i = 0; i < 10; i++) {
{
B_Add();
A_Add();
}
}
}
public void A_Add()
{
System.Diagnostics.Stopwatch oTime = new System.Diagnostics.Stopwatch(); //定义一个计时对象
oTime.Start(); //开始计时
for (int i = 0; i < 100000000; i++)
{
int_a = 1;
for (int j = 0; j < 30; j++)
{
int_a +=int_a;
}
}
oTime.Stop();
Console.WriteLine($"AAAAAAAAAAAAA A_Add {int_a} 程序的运行时间:{oTime.Elapsed.TotalSeconds} 秒 {oTime.Elapsed.TotalMilliseconds} 毫秒");
}
public void B_Add()
{
System.Diagnostics.Stopwatch oTime = new System.Diagnostics.Stopwatch(); //定义一个计时对象
oTime.Start(); //开始计时
for (int i = 0; i < 100000000; i++)
{
int_b = 1;
for (int j = 0; j < 30; j++)
{
int_b += int_b;
}
}
oTime.Stop();
Console.WriteLine($"B_Add {int_b} 程序的运行时间:{oTime.Elapsed.TotalSeconds} 秒 {oTime.Elapsed.TotalMilliseconds} 毫秒");
}
}
运行结果
其实很难看出volatile 变量和普通变量的差别,只能说从理论上来说volatile 变量的性能可能略次于普通变量
- 互锁构造
互锁构造主要是对Interlocked的使用,Interlocked类中方法都是原子的
也就是说,Interlocked add 或者read一个int数据的时候,是一定不会产生脏读的
利用Interlocked的特性,我们可以实现一个简单的自旋锁
public struct SimpleSpinLock{
private int m_Lock;
public void Enter(){
while (true){
if (Interlocked.Exchange(ref m_Lock, 1) == 0) return;
}
}
public void Leave(){
Volatile.Write(ref m_Lock, 0);
}
}
//下面是如何使用SimpleSpinLock的例子
public class Simple{
private SimpleSpinLock m_lock = new SimpleSpinLock();
public void AccessResource(){
m_lock.Enter();
//执行某些程序,只有一个线程可以进入这里
m_lock.Leave();
}
}
事件的代码中,如此使用自旋锁虽然性能很好,但是会大量的占用cpu时间,所以,仅仅是自旋还是不够的。