Lock关键字
说明:1, Lock关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。
2, 通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)
、lock (typeof (MyType))
和 lock ("myLock")
违反此准则。最佳做法是定义 private 对象来锁定。
namespace 线程同步
{
class Account
{
private Object thisLock = new Object(); //作为锁
int balance;
Random r = new Random();
public Account(int initial)
{
balance = initial;
}
int Withdraw(int amount)
{
// This condition will never be true unless the lock statement
// is commented out:
if (balance < 0)
{
throw new Exception("Negative Balance");
}
//注释掉lock就可能出问题
lock (thisLock)
{
if (balance >= amount) //如果 余额 >= 提款额
{
Console.WriteLine("提款前的余额 : " + balance);
Console.WriteLine("提款额 : -" + amount);
balance = balance - amount;
Console.WriteLine("提款后的余额 : " + balance);
Console.WriteLine("");
return amount;
}
else
{
return 0; // transaction rejected
}
}
}
public void DoTransactions()
{
for (int i = 0; i < 100; i++)
{
Withdraw(r.Next(1, 100));
}
}
}
class Test
{
static void Main()
{
Thread[] threads = new Thread[10];
Account acc = new Account(1000); //新建一个帐户,存款1000
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(new ThreadStart(acc.DoTransactions)); //新建10个线程进行取款
threads[i] = t;
}
for (int i = 0; i < 10; i++)
{
threads[i].Start(); //线程开始取款
}
}
}
}
ManualResetEvent
class Program
{
/// <summary>
/// ManualResetEvent建立时是把false作为start的初始状态,这个类用于通知另一个线程,让它等待一个或多个线程。
/// 如这个例子中,等待线程thread1线程调用mre.WaitOne(), 用于发信号的线程调用mre.Set().
/// </summary>
public static ManualResetEvent mre = new ManualResetEvent(false);
public static void trmain()
{
Thread tr = Thread.CurrentThread;
Console.WriteLine(tr.Name + " 开始第一波等待");
mre.WaitOne(); //等到什么时候呢?等到mre.Set()被调用
Console.WriteLine(tr.Name + " 第一波启动t");
mre.Reset(); //再次重置
Console.WriteLine(tr.Name + " 开始第二波等待");
mre.WaitOne(); //再次等待
Console.WriteLine(tr.Name + " 第二波启动");
for (int x = 0; x < 10; x++)
{
Thread.Sleep(1000);
Console.WriteLine(tr.Name + ": " + x);
}
}
static void Main(string[] args)
{
Thread thrd1 = new Thread(new ThreadStart(trmain));
thrd1.Name = "thread1";
thrd1.Start();
Thread thrd2 = new Thread(new ThreadStart(trmain));
thrd2.Name = "thread2";
thrd2.Start();
for (int x = 0; x < 10; x++)
{
Thread.Sleep(900);
Console.WriteLine("Main :" + x);
if (5 == x)
{
mre.Set(); //子线程的mre.WaitOne()可以执行了。第一次等待进程
//; //如果什么都不做呢,mre.Wait()那个线程就一直等在那里了?
}
}
while (thrd1.IsAlive)
{
Thread.Sleep(1000);
Console.WriteLine("Main: waiting for thread to stop...");
mre.Set(); //第二次通知等待进程
}
}
}
Interlocked类
说明:Interlocked 为多个线程共享的变量提供原子操作。 Interlocked 类提供这样一些方法,即同步对多个线程共享的变量的访问的方法。如果该变量位于共享内存中,则不同进程的线程就可以使用该机制。互锁操作是原子的 — 即整个操作是不能由相同变量上的另一个互锁操作所中断的单元。在现代处理器中,Interlocked 类的方法经常可以由单个指令来实现。因此,它们提供性能非常高的同步,并且可用于构建更高级的同步机制,例如自旋锁。
Exchange | 已重载。 以原子操作的形式将变量设置为指定的值。返回原始值。可用来对锁进行操作 |
class InterlocakedTest
{
//0 for false, 1 for true.
private static int usingResource = 0;
private static Object currentMso;
private static Object globalMso = new Object(); //全局资源
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.把它置为1。如果返回的原始值是0。代表获得锁
if(0 == Interlocked.Exchange(ref usingResource, 1)) //获得锁
{
Console.WriteLine("{0} 获得锁", 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} 退出锁", Thread.CurrentThread.Name);
//Release the lock
Interlocked.Exchange(ref usingResource, 0); //解锁
return true;
}
else
{
Console.WriteLine(" {0} 被拒绝", Thread.CurrentThread.Name);
return false;
}
}
}
说明:Interlocked.Increment 以原子操作的形式递增指定变量的值并存储结果。
Interlocked.Decrement 以原子操作的形式递减指定变量的值并存储结果。
namespace 线程同步
{
class InterlockedTest2
{
static void Main()
{
Thread thread1 = new Thread(new ThreadStart(ThreadMethod));
Thread thread2 = new Thread(new ThreadStart(ThreadMethod));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
// Have the garbage collector run the finalizer for each
// instance of CountClass and wait for it to finish.
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("UnsafeInstanceCount: {0}" +
"/nSafeCountInstances: {1}",
CountClass.UnsafeInstanceCount.ToString(),
CountClass.SafeInstanceCount.ToString());
}
static void ThreadMethod()
{
CountClass cClass;
// Create 100,000 instances of CountClass.
for (int i = 0; i < 100000; i++)
{
cClass = new CountClass();
}
}
}
//unsafeInstanceCount,safeInstanceCount是类变量,所以两个线程对它的访问要保证是原子的。
class CountClass
{
static int unsafeInstanceCount = 0;
static int safeInstanceCount = 0;
static public int UnsafeInstanceCount
{
get { return unsafeInstanceCount; }
}
static public int SafeInstanceCount
{
get { return safeInstanceCount; }
}
public CountClass()
{
unsafeInstanceCount++;
Interlocked.Increment(ref safeInstanceCount);
}
~CountClass()
{
unsafeInstanceCount--;
Interlocked.Decrement(ref safeInstanceCount);
}
}
}
Monitor监视器类
说明: 1, Monitor 对象通过使用 Monitor.Enter、Monitor.TryEnter 和 Monitor.Exit 方法对特定对象获取锁和释放锁来公开同步访问代码区域的能力。(这点看,Lock的代码区域类似用Monitor.Enter和Monitor.Exit内的代码段)在对代码区域获取锁后,就可以使用 Monitor.Wait、Monitor.Pulse 和 Monitor.PulseAll 方法了。如果锁被暂挂,则 Wait 释放该锁并等待通知。当 Wait 接到通知后,它将返回并再次获取该锁。Pulse 和 PulseAll 都会发出信号以便等待队列中的下一个线程继续执行。
2, Monitor 将锁定对象(即引用类型),(Lock可以获取值类型的锁吗?应该也不可以吧)而非值类型。尽管可以向 Enter 和 Exit 传递值类型,但对于每次调用它都是分别装箱的。因为每次调用都创建一个独立的对象,所以 Enter 永远不会阻止,而且它要保护的代码并没有真正同步。
3, 注意到 Monitor 和 WaitHandle 对象在使用上的区别是非常重要的。Monitor 对象是完全托管、完全可移植的,并且在操作系统资源要求方面可能更为有效。WaitHandle 对象表示操作系统可等待对象,对于在托管和非托管代码之间进行同步非常有用,并公开一些高级操作系统功能。
**4. Monitor.enter 就是lock{ ;而Monitor.exit 就是lock 结束}
lock (对象){ // 就是Monitor.enter(对象)
} //就是Monitor.exit .
在lock结束之前所有线程对该对象的操作都将被强制睡眠直到解锁。
但要特别注意的是 Monitor 有更强大的功能,比如 Monitor.TryEnter。Monitor.TryEnter方法就是获取对象的排它锁是否成功。Monitor.TryEnter无论有没有获取对象锁都会返回结果。Monitor.TryEnter在没有锁对象的情况下进行等待,而lock就处于等待直到完成。在执行过程方法或对象是所有线程唯的,那只好用lock;如果线程对锁操作可以放弃或者有别的选择的情况可以采用Monitor.TryEnter。
using System;
using System.Threading;
// Note: The class whose internal public member is the synchronizing
// method is not public; none of the client code takes a lock on the
// Resource object.The member of the nonpublic class takes the lock on
// itself. Written this way, malicious code cannot take a lock on
// a public object.(这个类的写法,可以防止恶意代码在public对象加锁)
public class SyncResource
{
public void Access(Int32 threadNum)
{
// Uses Monitor class to enforce synchronization.
lock (this)
{
// Synchronized: Despite the next conditional, each thread
// waits on its predecessor.
if (threadNum % 2 == 0)
Thread.Sleep(2000);
Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum);
Thread.Sleep(200);
Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum);
}
}
}
// Without the lock, the method is called in the order in which threads reach it.
public class UnSyncResource
{
public void Access(Int32 threadNum)
{
// Does not use Monitor class to enforce synchronization.
// The next call throws the thread order.
if (threadNum % 2 == 0)
Thread.Sleep(2000);
Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
Thread.Sleep(200);
Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum);
}
}
public class App
{
public static object obj = new object();
static Int32 numAsyncOps = 5;
static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
static SyncResource SyncRes = new SyncResource();
static UnSyncResource UnSyncRes = new UnSyncResource();
public static void Main()
{
for (Int32 threadNum = 5; threadNum > 0; threadNum--)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(SyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Console.WriteLine("/t/nAll synchronized operations have completed./t/n");
// Reset the thread count for unsynchronized calls.
numAsyncOps = 5;
for (Int32 threadNum = 0; threadNum < 5; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UnSyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Console.WriteLine("/t/nAll unsynchronized thread operations have completed.");
}
// The callback method's signature MUST match that of a
// System.Threading.TimerCallback delegate (it takes an Object
// parameter and returns void).
static void SyncUpdateResource(Object state)
{
// This calls the internal synchronized method, passing
// a thread number.
SyncRes.Access((Int32)state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread
// will access the value **before** another thread's incremented
// value has been stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
asyncOpsAreDone.Set();
// Announce to Main that in fact all thread calls are done.
}
// The callback method's signature MUST match that of a
// System.Threading.TimerCallback delegate (it takes an Object
// parameter and returns void).
static void UnSyncUpdateResource(Object state)
{
// This calls the internal synchronized method, passing a thread number.
UnSyncRes.Access((Int32)state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread
// will access the value **before** another thread's incremented
// value has been stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
asyncOpsAreDone.Set();
// Announce to Main that in fact all thread calls are done.
}
}