这里要说的就是多线程的锁的问题了
锁:作用在于实现线程间的同步问题,最典型的是售票问题
1,InterLocked
提供的都是静态方法,用来同步对多个共享变量的访问,包括以原子方式递增,递减,比较和替换值得方法
原子操作:方法在一个操作中完成它的功能的操作,递增操作分为两步进行,1,植被增加,2更新值被存储
静态方法
Long Increment(ref long value)
int Increment(ref int value)
递增指定的值,然后返回更新后的值
int Decrement(ref int value)
long Decrement(ref long value)
递减指定的值,然后返回更新后的值
int Exchange(ref int location ,int newvalue) 其他的重载还有float,Object
把location的值设置为newvalue,可以交换整型值,单精度和对象类型返回location的原始值
CompareExchange(ref object destvalue,object replacevalue,object compareValue)
比较destvalue和comparevalue 如果值相同,destvalue用replacevalue代替 返回destvalue的值
监控器锁
Method1 Monitor.Enter(obj) 获取锁 Monitor.Waite(obj) 阻塞当前线程 Monitor.Exite(obj) 释放锁 Method2 Monitor.Enter(obj) 获取锁 Monitor.Pulse(obj) 唤醒Method1中Waite的线程 Monitor.Exite(obj) 释放锁
Monitor 类使用锁的概念提供对象的同步代码块,监控锁和临界区相似,只有拥有对象监控锁的对象才能访问对象
Enter(object obj)
视图获得指定对象的监控锁,如果另一个线程持有锁,该方法会阻塞,如果obj为空则抛出异常
Exite(object obj)
释放指定对象的监控器锁,现在,另一个线程就可以获得锁了,若果obj为空,或者当前线程并不持有指定对象的锁,系统将抛出异常
Pulse(object obj)
被持有指定对象的锁的线程调用,以通知等待队列中的下一个线程在对象状态方面的变化,这下一个线程被移预备队列中,调用
Pluse方法的线程释放锁,允许预备队列中下一个线程获得该锁,该方法必须从一个同步代码块中调用,如果Obj是空引用则抛异常
PulseAll(Object obj)
被持有指定对象的锁的线程调用,以通知等待队列的所有线程在对象状态方面的变化,等待队列中的所有线程被移到预备队列中,调用
PluseAll方法的线程释放锁,允许预备队列中的下一个线程获取到该锁,该方法必须从一个同步代码块中调用,同上抛出异常
TryEnter(Object obj)
视图在特定的雕件下获取指定对象的监控锁,第一个版本在视图获取锁时不会阻塞,第二个和第三个版本就会阻塞,但值阻塞指定的时间间隔,如果方法成功返回true,否则返回false
wait(Object obj)
释放监控器锁并把调用线程放在等待队列中,如果另一个线程调用了pulse和pulseall方法该线程被移到预备队列中
class Program
{ Queue queue = new Queue(); static void Main(string[] args)
{
Program p = new Program();
Thread t1= new Thread(p.Add);
Thread t2= new Thread(p.Remove);
t2.Priority = ThreadPriority.Highest;
t1.Start();
t2.Start(); Console.ReadKey();
} public void Add()
{
for (int i = ; i < ; i++)
{
Monitor.Enter(queue); this.queue.Enqueue(i); Console.WriteLine("当前数组数量:" + queue.Count); Monitor.Pulse(queue); Monitor.Exit(queue);
}
} public void Remove()
{ for (int i = ; i < ; i++)
{
lock (queue)
{
if (queue.Count < )
Monitor.Wait(queue);
queue.Dequeue(); }
}
}
WaitHandle类
是Win32同步句柄的基类,它用来管理对共享资源的访问,它提供了等待直到对象变为有新号状态的方法
派生类 Mutex,AutoResetEvent,ManualResetEvent
InvalidHandle 代表一个无效的同步句柄
WaitTimeout敞亮是一个整形,之处对WaitAny方法的调用已经超时
属性
Handle 访问和设置本机操作系统句柄
公共静态方法
WaitAll(WaitHandle[] waitHandles)
当指定数组中的所有waithandle对象变为有信号状态时,或者指定的超时时间已到,WaitAll返回
WaitAny (WaitHandle[] waitHandles)
当指定数组中的任意waithandle对象变为有信号状态时,或者指定的超时时间已到,WaitAny返回
Close()
关闭调用句柄,该方法调用Dispose方法,传递给他一个ture
WaitOne()-------请求互斥锁
在调用WaitHandle对象处于有信号状态,或者指定超时时间已到时返回。无参的签名将无限期阻塞
直到当前的WaitHandle变为有信号状态,如果WaitHandle是空引用则抛异常
Mutex类
代表一个允许排斥访问共享资源的互斥锁,一旦一个线程获得了互斥锁,想访问共享资源的任何其他线程就被挂起直到锁被释放。WaitHandle类的WaitOne方法可以用来请求互斥锁。可以调用ReleaseMutex方法来释放锁
ReleaseMutex方法释放一次互斥锁,也就是说调用了几次Wait()方法,就需要调用几次ReleaseMutex方法
class MutexDemo
{
private Mutex mutex = new Mutex(); public MutexDemo()
{
mutex = new Mutex();
} public static void Run()
{
MutexDemo demo = new MutexDemo();
Thread th1 = new Thread(demo.Increment);
Thread th2 = new Thread(demo.Increment); th1.Name = "One";
th2.Name = "Two"; th1.Start();
th2.Start(); Console.ReadKey(); } public void Increment()
{
for (int i = ; i < ; i++)
{
Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
Thread.Sleep();
}
}
结果为
One--0
Two--0
One--1
Two--1
One--2
Two--2
One--3
Two--3
我们想要的是让第一个线程打印完然后去打印第二个线程,那么
class MutexDemo
{
private Mutex mutex = new Mutex(); public MutexDemo()
{
mutex = new Mutex();
} public static void Run()
{
MutexDemo demo = new MutexDemo();
Thread th1 = new Thread(demo.Increment);
Thread th2 = new Thread(demo.Increment); th1.Name = "One";
th2.Name = "Two"; th1.Start();
th2.Start(); Console.ReadKey(); } public void Increment()
{
mutex.WaitOne();
for (int i = ; i < ; i++)
{
Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
Thread.Sleep();
}
mutex.ReleaseMutex();
}
这样的结果就是我们想要的了
ReaderWriterLock类
该类为读写数据的线程提供锁,它支持嵌套的读锁和写锁,并允许宁改变与线程关联的类型,或者把锁回复到前一个状态,ReaderWriterLock对象可以获得一个读锁或者一个写锁,但不能同时获得两个锁。ReaderWriterLock对象能够支持至少一个写线程,但可支持多个读线程
属性
bool IsReaderLockHeld
bool IsWriterLockHeld
int WriterSeqNum
如果当前线程持有读锁,IsReaderLockHeld返回true;
如果当前线程持有写锁,IsWriterLockHeld返回true
WriterSeqNum检索和ReaderWriterLock相关的序列号,如果ReaderWriterLock没有获得写锁,序列号就是1,如果获得了,序列号就是2
void AcquireReaderLock()
AcquireReaderLock视图为当前线程获得读锁,他将在指定的事件内进行操作,该方法支持嵌套的读锁,如果另一个线程有写锁,调用线程将阻塞
void AcquireWriterLcok()
AcquireWriterLcok 视图为当前线程获得写锁,在指定的事件内进行这个操作,该方法支持嵌套的写锁,如果另一个线程有读锁,调用线程将阻塞,如果他本身持有读锁,将造成死锁,可以使用UpgradeToWriterLock方法防止死锁的出现
AnyWritersSince(int seqNum)
如果自从获得该实例的序列号以来发生了任何中间写操作,AnyWritersSince返回true
ReleaseLock
ReleaseReaderLock
ReleaseWriterLock
释放锁的方法
class RWDemo
{
ReaderWriterLock rwlock;
int value;
byte[] buf = new byte[];
MemoryStream ms; public RWDemo()
{
rwlock=new ReaderWriterLock();
value = ;
ms = new MemoryStream(); } public static void Run()
{
RWDemo demo = new RWDemo(); Thread one = new Thread(demo.ReadSome);
Thread two = new Thread(demo.ReadSome);
Thread three = new Thread(demo.WriteSome); one.Start();
two.Start();
three.Start(); } public void WriteSome()
{
rwlock.AcquireReaderLock(-);
Thread.Sleep();
buf[] = (byte)value;
ms.Position = ;
ms.Write(buf, , buf.Length);
Console.WriteLine("value write is "+value);
rwlock.ReleaseReaderLock();
} public void ReadSome()
{
Thread.Sleep();
rwlock.AcquireWriterLock(-);
ms.Position = ;
Console.WriteLine("value read is " + ms.ReadByte());
rwlock.ReleaseWriterLock();
}
}