1,Volate.Write和Volate.Read:
bool complete = false; var t = new Thread(() => { bool toggle = false; while (!complete) { toggle = !toggle; } }); t.Start(); Thread.Sleep(1000); complete = true; t.Join(); Console.Read();
注意:该代码会无限循环,(在Release优化模式下).使用Volatile就是解决这个问题的.Volatile大致时做了这么几件事情.VolateRead(Ref Value),首先,会进行一次内存读写(而非缓存),然后,在其后面的读写操作必须在其完成后再执行.
Write操作:再执行时强制写入内存.并且那些再其之前的操作需要再Write之前发生.
也就是说,Read优先在最前面.Write往往放在最后面.(这两个函数最要用于实时读取或者写入内存中的实际值.相当于PLC中的:P功能).
2,InterLock(互锁构造).
//Increment():原子性对一个32位整数进行+1操作. //Decrement:原子性的对一个32位整数进行-1操作. //Add(),原子性的对一个32位整数进行加减操作 //Exchange(a,b)原子性的对一个32位整数进行赋值,并返回原来值 //CompareExchange(a,value,b)如果 a==b,则 a=value,并且返回a;
利用异步模式进行多服务器请求的方式:
internal sealed class MultiWebRequests { private AsyncCoordinator m_ac = new AsyncCoordinator(); private Dictionary<string, object> m_servers = new Dictionary<string, object> { {"https://www.baidu.com",null }, {"https://192.168.0.11",null } }; public MultiWebRequests(int timeout=Timeout.Infinite) { var httpClient = new HttpClient(); foreach(var server in m_servers.Keys) { m_ac.AboutToBegin(1); httpClient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task)); } m_ac.AllBegun(AllDone, timeout); } private void AllDone(CoordinationStatus status) { switch (status) { case CoordinationStatus.Cancel: Console.WriteLine("operation canceled"); break; case CoordinationStatus.Timeout: Console.WriteLine("Operation timed-out"); break; case CoordinationStatus.AllDone: Console.WriteLine("operation completed;results below:"); foreach(var server in m_servers) { Console.WriteLine("{0} ", server.Key); object result = server.Value; if(result is Exception) { Console.WriteLine("failed due to {0}", result.GetType().Name); } else { Console.WriteLine("returned {0:N0} bytes.", result); } } break; } } private void ComputeResult(string server, Task<byte[]> task) { object result; if (task.Exception != null) { result = task.Exception.InnerException; } else { result = task.Result.Length; } // m_servers[server] = result; m_ac.JustEnded(); } public void Cancel() { m_ac.Cancel(); } } enum CoordinationStatus { Cancel,Timeout,AllDone} internal sealed class AsyncCoordinator { private int m_opCount = 1; private int m_statusReported = 0; private Action<CoordinationStatus> m_callback; private Timer m_timer; //协定异步计数器的大小 public void AboutToBegin(Int32 opsToAdd = 1) { Interlocked.Add(ref m_opCount, opsToAdd); } //判断是否异步计数器为0,如果为0,表明所有操作完成 public void JustEnded() { if(Interlocked.Decrement(ref m_opCount)==0) ReportStatus(CoordinationStatus.AllDone); } //当,所有异步操作执行时,执行该函数,用来启动计数器的判断模式. //当启动限时操作时,则传递委托执行当超时时,报告超时异常. public void AllBegun(Action<CoordinationStatus> callback,int timeout=Timeout.Infinite) { m_callback = callback; if (timeout != Timeout.Infinite) m_timer = new Timer(TimeExpired, null, timeout, Timeout.Infinite); JustEnded(); } //超时时,则执行报告超时. private void TimeExpired(object state) { ReportStatus(CoordinationStatus.Timeout); } //调用cancel()时,则报告Cancel(). public void Cancel() { ReportStatus(CoordinationStatus.Cancel); } //使用m_statusReorted,进行原子操作进行竞争.从而只让某个委托函数只执行一次. private void ReportStatus(CoordinationStatus status) { if (Interlocked.Exchange(ref m_statusReported, 1) == 0) m_callback(status); } }
利用Task方法,在每次请求数据结束后进行数据处理(是否异常,如果不是异常则返回结果,如果是异常则返回异常).
其次,利用协调类的m_OpCount来进行操作判断处理,判断是否所有操作处理完毕
再次,利用竞争从而只形成一次报告生成.
3,简单自旋锁:
internal struct SimpleSpinLock { private int m_ResourceInUse; public void Enter() { while (Interlocked.Exchange(ref m_ResourceInUse, 1) != 0) { //黑科技//简单自旋锁适用计算量不大的场合. } } public void Leave() { Volatile.Write(ref m_ResourceInUse, 0); } }
- 1,利用Interlock进行原子操作保证
- 2,使用Volatile保证内存写入一致.
- 3,问题,可能形成
Thread.Yeild 该方法是在 .Net 4.0 中推出的新方法,它对应的底层方法是 SwitchToThread。Yield 的中文翻译为 “放弃”,这里意思是主动放弃当前线程的时间片,并让操作系统调度其它就绪态的线程使用一个时间片。但是如果调用 Yield,只是把当前线程放入到就绪队列中,而不是阻塞队列。如果没有找到其它就绪态的线程,则当前线程继续运行。 优势:比 Thread.Sleep(0) 速度要快,可以让低于当前优先级的线程得以运行。可以通过返回值判断是否成功调度了其它线程。 劣势:只能调度同一个处理器的线程,不能调度其它处理器的线程。当没有其它就绪的线程,会一直占用 CPU 时间片,造成 CPU 100%占用率 Thread.Sleep(0) Sleep 的意思是告诉操作系统自己要休息 n 毫秒,这段时间就让给另一个就绪的线程吧。当 n=0 的时候,意思是要放弃自己剩下的时间片,但是仍然是就绪状态,其实意思和 Yield 有点类似。但是 Sleep(0) 只允许那些优先级相等或更高的线程使用当前的CPU,其它线程只能等着挨饿了。如果没有合适的线程,那当前线程会重新使用 CPU 时间片。 优势:相比 Yield,可以调度任何处理器的线程使用时间片。 劣势:只能调度优先级相等或更高的线程,意味着优先级低的线程很难获得时间片,很可能永远都调用不到。当没有符合条件的线程,会一直占用 CPU 时间片,造成 CPU 100%占用率。 Thread.Sleep(1) 该方法使用 1 作为参数,这会强制当前线程放弃剩下的时间片,并休息 1 毫秒(因为不是实时操作系统,时间无法保证精确,一般可能会滞后几毫秒或一个时间片)。但因此的好处是,所有其它就绪状态的线程都有机会竞争时间片,而不用在乎优先级。 优势:可以调度任何处理器的线程使用时间片。无论有没有符合的线程,都会放弃 CPU 时间,因此 CPU 占用率较低。 劣势:相比 Thread.Sleep(0),因为至少会休息一定时间,所以速度要更慢。 Thread.Sleep(0) vs Sleep(1) vs Yeild
4,InterLocked Anything
public delegate Int32 Morpher<TResult, TArgument>(int startValue, TArgument argument, out TResult morphResult); //将一个委托方法变成原子操作方式: public static TResult Morph<TResult,TArgument>(ref int target,TArgument argument,Morpher<TResult,TArgument> morpher) { //方程需要的结果 TResult morphResult; //进行操作,如果数据有变化就从新操作. int currentVal = target, startVal, desiredVal; do { startVal = currentVal; desiredVal = morpher(startVal, argument, out morphResult); currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal); } while (startVal != currentVal); return morphResult; }
这是一个原子方法的扩展版本.也就是用于包装一个原子方法,保证其被更新.
这是一个实战的列子
public static void go() { int count = 0; int m_count = 0; List<Task> tasks = new List<Task>(); for(var i = 0; i < 3; i++) { var t = Task.Run(() => { for (var ii = 0; ii < 50000; ii++) { //对于count就是进行对其的原子操作.(使用Count__对其进行一次原子操作,在这里是+1) //对于m_count,其是一次伴随操作. m_count=Morph<int, int>(ref count, 1, Count__); } }); tasks.Add(t); } Task.WaitAll(tasks.ToArray()); Console.WriteLine(count+" "+m_count); } private static int Count__(int startValue, int argument, out int morphResult) { var result = startValue + 1; morphResult = result; return result; }
5,
内核模式构造
- WaitHandle
- EventWaitHandle
- AutoResetEvent//每次运行完WaitOne自动设定成Reset.
- ManualResetEvent//需要手动设定成Reset.
- Semaphore
- Mutex
WaitHandle用于包装一个内核对象句柄.其常用方法:
WaitOne():当传递true的时候(Set())则不堵塞,否则则堵塞线程
:当返回true,表示调用了Set(),否则,当超时时,则返回False();
Set():使堵塞的WaitOne()使能为可以运行.
ReSet(): 使运行的WaitOne() 设置成堵塞.
OpenExsiting(string str)可以获取已经创建的内核句柄.
new
一个交替打印1--100的方式:
public static void go3() { var waiter1 = new AutoResetEvent(false); var waiter2 = new AutoResetEvent(false); var m_count = 1; Task.Run(() => { while (m_count <= 99) { //获取set方法或者超时,如果超时返回false,否则返回true,表示set()方法打通. var wt = waiter1.WaitOne(100); Console.WriteLine("thread1 is " + (m_count++) + "waiter is" + wt); waiter2.Set(); } }); Task.Run(() => { while (m_count <= 100) { var wt= waiter2.WaitOne(100); Console.WriteLine("thread2 is " + (m_count++)+"waiter is"+wt); waiter1.Set(); } }); }
Semaphore构造: 其实是内核维护的Int32变量.当=0的时候,在信号等待的线程阻塞,不为0的时候,解除阻塞.
构建一个Semaphore对象 New Semaphore(0,1,”name”,ref bool create_new):
创建一个独一无二的内核对象"name"如果是本线程创建,则create_new=true,否则 false;
当使用WaitOne()方法的时候,如果 Semaphore对象内部管理的信号量=0,则堵塞线程,否则 不堵塞.
每使用WaitOne()方法一次,就将信号量减1.
每使用release(count)就使信号量+count.
和AutoResetEvent不同的地方在于,多次Set(),也只会使一个线程恢复非堵塞,而Semaphore可能会造成释放多个线程.
利用AutoReset和Semaphore都可以进行自旋锁的构造.
public class SimpleLock1:IDisposable { private Semaphore m_lock = new Semaphore(1, 1); public void Enter() { m_lock.WaitOne(); } public void Leave() { m_lock.Release(); } public void Dispose() { m_lock.Close(); } }
Mutex:
互诉锁线程使用Mutex.WaitOne()方法等待C# Mutex对象被释放,如果它等待的C# Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间,其他想要获取这个C# Mutex对象的线程都只有等待
简单点说,这个锁监视当前线程:使用WaitOne()的方法,在运行后,会使其当前线程监视值+1,使用Release()方法,会使监视值-1,当,监视值=0的时候,才可以由其他线程获取使用权.(一般避免使用这个锁,而改用自定义的递归锁(对象递归).
public static void go6() { //参数指示当前运行的线程是否获取Mutex锁,如果选择true的话,则需要释放一次,否则别的线程无法获取. var m_lock = new Mutex(false); var m_count = 1; //注意递归的用法.必须当整个线程运行完毕后,其他的线程才能够获得锁的所有权才会运行下一步. Task.Run(() => { //如果没线程在用就获取锁. while (m_count <= 100) { if (m_count % 2 == 1) { m_lock.WaitOne(); Console.WriteLine("Thread{0} is Print {1}", Thread.CurrentThread.ManagedThreadId, m_count++); m_lock.ReleaseMutex(); } } }); Task.Run(() => { while (m_count <= 100) { if (m_count % 2 == 0) { m_lock.WaitOne(); Console.WriteLine("Thread{0} is Print {1}", Thread.CurrentThread.ManagedThreadId, m_count++); m_lock.ReleaseMutex(); } } }); }
模拟递归锁
public sealed class RecursiveAutoResetEvent:IDisposable { private AutoResetEvent m_lock = new AutoResetEvent(true); private int m_owningThreadId = 0; private int m_recursionCount = 0; public void Enter() { Int32 currentThreadId = Thread.CurrentThread.ManagedThreadId; //如果当前线程是原进程 if(currentThreadId==m_owningThreadId) { m_recursionCount++; return; } //当前进程是第一次进入 m_lock.WaitOne(); //当前线程第一次拥有锁 m_owningThreadId = currentThreadId; m_recursionCount = 1; } public void Leave() { Int32 currentThreadId = Thread.CurrentThread.ManagedThreadId; if (currentThreadId != m_owningThreadId || m_recursionCount < 1) throw new InvalidOperationException("error thread"); if(--m_recursionCount==0) { m_owningThreadId = 0; m_lock.Set(); } } public void Dispose() { m_lock.Dispose(); } }