C#线程安全使用(三)
在讲CancellationTokenSource之前我决定先讲一下lock和Interlocked,如果能很好的理解这两个,再去理解CancellationTokenSource就会方便很多,由于我也是后起使用多线程,使用的时候就是直接运用FramWork4的东西,这样导致了很多东西学起来很吃力,当回顾了以前的知识点后,发现新出的东西如此好理解。
先看一下Lock的使用,下面是一个例子。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace LockConsole { class Program { static object lockint = 0; static void Main() { Task[] tlist=new Task[10]; Thread[] threads = new Thread[10]; for (int i = 0; i < 10; i++) { var action = new Action<object>(Withdraw); Task t = new Task(action, i); tlist[i] = t; //Thread t = new Thread(new ThreadStart(acc.DoTransactions)); //threads[i] = t; } for (int i = 0; i < 10; i++) { tlist[i].Start(); //threads[i].Start(); } Console.Read(); } static void Withdraw(object i) { int amount = int.Parse(i.ToString()); int balance = 1000; lock (lockint)//删除lock就会造成 线程混乱 { if (balance >= amount) { Console.WriteLine("当前线程{0},当前状态{1}", Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.ThreadState + "Balance before Withdrawal : " + balance); Console.WriteLine("当前线程{0},当前状态{1}", Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.ThreadState + "Amount to Withdraw : -" + amount); balance = balance - amount; Console.WriteLine("当前线程{0},当前状态{1}", Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.ThreadState + "Balance after Withdrawal : " + balance); } } } } }
输出结果如下,他的线程都是一个接一个的,线程11后是线程12,然后是13,14,11等。每个线程都是等到执行完了下一个才执行。
在看一下没有Lock的结果,如下图,线程是混乱的,12线程的函数没执行完13就开始了。
有了上面的例子,Lock就很好理解了,他是为了保障资源同一时间只被一个线程使用,虽然该例子中没有使用Lock的资源,但线程还是一个接一个的执行,因为使用了lock线程就会一个接一个执行。
接下来看一下interLock,这是一个MSDN的例子,我觉得不错,他的意思是,把usingResource作为一个锁,当多线程开始运行,函数UseResource()需要判断usingResource的值,当usingResource等于0的时候,当前线程不运行,否则运行,当本线程运行时,要修改usingResource的值为1,这样确保其他线程不运行,即同一时间只运行一个线程。
如果这样需求用到开发中,会出现一个问题,那就是当一个线程改变usingResource的值的一瞬间,别的线程读取了usingResource的值,那这个线程也被运行了。
这时就需要InterLock了。代码如下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MyInterlockedExchangeExampleClass { class MyInterlockedExchangeExampleClass { //0 for false, 1 for true. private static int usingResource = 0; private const int numThreadIterations = 5; private const int numThreads = 10; static void Main() { Thread myThread; Random rnd = new Random(); for (int i = 0; i < numThreads; i++) { myThread = new Thread(new ThreadStart(MyThreadProc)); myThread.Name = String.Format("Thread{0}", i + 1); //Wait a random amount of time before starting next thread. Thread.Sleep(rnd.Next(0, 1000)); myThread.Start(); } } private static void MyThreadProc() { for (int i = 0; i < numThreadIterations; i++) { UseResource(); //Wait 1 second before next attempt. Thread.Sleep(1000); } } //A simple method that denies reentrancy. static bool UseResource() { //0 indicates that the method is not in use. //原始值是0,判断是时候使用原始值,但判断后值为1,进行了设置 if (0 == Interlocked.Exchange(ref usingResource, 1)) { Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name); //Code to access a resource that is not thread safe would go here. //Simulate some work Thread.Sleep(500); Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name); //Release the lock Interlocked.Exchange(ref usingResource, 0); return true; } else { Console.WriteLine(" {0} was denied the lock", Thread.CurrentThread.Name); return false; } } } }
理解了lock和interlock后下一章讲解CancellationTokenSource。
----------------------------------------------------------------------------------------------------
注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!