











方法同步:(使用MethodImplAttribute类)
该类属于命名空间
System.Runtime.CompilerServices.作用是同时只允许一个线程调用该方法。
使用方法如下:
[MethodImpl(MethodImplOptions.Synchronized)]
public void
doSomeWork(){......}
代码区同步:
1 Monitor类
使用
Monitor 锁定对象类型而不是值类型。
它是未绑定的,也就是说可以直接从任何上下文调用它。
不能创建
Monitor 类的实例。
使用 Enter 和 Exit
方法标记临界区的开头和结尾。
建议:先Enter,然后将这些指令放在 try 块中,将 Exit 指令放在 finally
块中。
使用方法:
Monitor.Enter(this);
tatch{//Code}
catch{//code}
finally{Monitor.Exit(this);}
关于Wait()和Pulse():当执行Wait方法,正在访问该对象的线程就会进入等待状态,直到等到一个唤醒的信号。
Pulse&PulseAll方法用于给等待线程发送信号。
当锁定非自身对象的时候,可以使用wait使得使用该对
象的线程暂时睡眠,pulse使得唤醒其他使用该对象的线程并让自身睡眠
关于TryEnter():该方法与 Enter类似,区别在于当无法获得锁定时,会在指定间隔时间之后重新尝试获得锁定。
2
lock
最常用的方法:
lock(this)
{//code
}
3
关于线程安全包装器
假设有myclass类,该类包含void方法DoSome,可以写如下线程安全包装器:
public class Wrapper
{
private myclass
_m;
public Wrapper(){_m=new myclass();}
public void DoSome()
{
lock(_m){this._m.DoSome();}
}
}
说明:
这里获得锁的不是Wrapper对象,我们需要线程安全的是
myclass而不是包装器
线程安全包装器一般在处理第三方类的时候使用
4
ReadWriteLock类
在任一特定时刻,它允许多个线程同时进行读访问,或者允许单个线程进行写访问。就是说,当读锁存在时,
不可获得写锁,可获得读锁。当所有锁释放时可以获得写锁。当写锁存在时无法获得任何锁。
在资源不经常发生更改的情况下,
ReaderWriterLock 所提供的吞吐量比简单的一次只允许一个线程的锁(如
Monitor)更高。在多数访问为读访问,而写访问频率较低、持续时间也比较短的情况下,ReaderWriterLock
的性能最好。多个读线程与单个写线程交替进行操作,所以读线程和写线程都不会长时间阻塞。
一个线程可以持有读线程锁或写线程锁,但不能同时持有两者。若要获取写线程锁,请使用UpgradeToWriterLock和
DowngradeFromWriterLock,而不要通过释放读线程锁的方式获取。
使用方法:
public
class Account
{
int val ;
ReaderWriterLock rwl = new ReaderWriterLock();
public int Read()
{
rwl.AcquireReaderLock(Timeout.Infinite);
int iRet =
0;
try
{
iRet = val;
}
finally
{
rwl.ReleaseReaderLock();
}
return
iRet;
}
public void Write(int
x)
{
rwl.AcquireWriterLock(Timeout.Infinite);
try
{
val = x;
}
finally
{
rwl.ReleaseWriterLock();
}
}
}
注意:
I 锁的获取设定了超时时间(下文会说明)
II
锁的释放在finally代码块中
关于获取锁的超时时间:
多数在
ReaderWriterLock
上获取锁的方法都采用超时值。使用超时可以避免应用程序中出现死锁。例如,某个线程可能获取了资源1上的写线程锁,然后请求资源2上的读线程锁;同时,另
一个线程获取了资源2上的写线程锁,并请求资源1上的读线程锁。如果不使用超时,这两个线程将出现死锁。
如果超时间隔过期并且没有授
予锁请求,则此方法通过引发 ApplicationException
将控制返回给调用线程。线程可以捕捉此异常并确定下一步要进行的操作。
值 说明
-1
Infinite.
0 无超时。
> 0
要等待的毫秒数。
手控同步:
1
Mutex
该类从WaitHandle派生
除了可以用于进程间同步
Metux非常类似于
Monitor.
当两个或更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。Mutex
是同步基元,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥
体。
WaitOne 方法:请求互斥体的所属权。拥有互斥体的线程可以在对 Wait
的重复调用中请求相同的互斥体而不会阻塞其执行。
ReleaseMutex
方法:释放互斥体的所属权。如果线程在拥有互斥体期间正常终止,则互斥体状态设置为终止,并且下一个等待线程获得所属权。如果没有线程拥有互斥体,则互斥
体状态为终止。
public class Account
{
int val
;
Mutex m;
public Account(){m=new
Mutex(true,"test");}
public int Read()
{
int iRet = 0;
m.WaitOne();
try
{
iRet = val;
}
finally
{
m.ReleaseMutex();
}
return iRet;
}
public
void Write(int x)
{
m.WaitOne();
try
{
val = x;
}
finally
{
m.ReleaseMutex();
}
}
}
2
ManualResetEvent类与AutoResetEvent类
该类从WaitHandle派生
该对象只
能拥有2种状态:有信号或无信号
Set和Reset方法返回一个BOOL,表示状态是否成功进行修改
把状态修改
成无信号,使用Reset方法,修改成有信号,使用Set方法
WaitOne方法:在有信号时不等待,无信号时等待。2个参数,第一
个是等待毫秒数,第二个BOOL,如果已经在上下文同步域想要退出,或者要重新获得同步上下文,就设置为True。
在
AutoResetEvent时,WaitOne会改变状态。使用ManualResetEvent时不会。
3
InterLocked
此类的方法可以防止可能在下列情况发生的错误:线程正在更新可由其他线程访问的变量时,计划程序切换上下文;或者两个线程在不同的处理器
上同时执行。此类的成员不引发异常。
Increment 和
Decrement
方法递增或递减变量并将结果值存储在单个操作中。在大多数计算机上,增加变量操作不是一个原子操作,需要执行下列步骤:
(1).将实例变量中的值加载到寄存器中。
(2).增加或减少该值。
(3).在实例变量中存储该值。
如果不使用 Increment 和
Decrement,线程会在执行完前两个步骤后被抢先。然后由另一个线程执行所有三个步骤。当第一个线程重新开始执行时,它改写实例变量中的值,造成第
二个线程执行增减操作的结果丢失。
Exchange
方法自动交换指定变量的值。CompareExchange
方法组合了两个操作:比较两个值以及根据比较的结果将第三个值存储在其中一个变量中。比较和交换操作按原子操作执行。
有InterLocked公开的Exchange和CompareExchange方法采用可以存储引用的Object类型的参数。但
是,类型安全要求将所有参数严格类型化为Object;不能对其中一个方法的调用中简单地将对象强制转换为Object,换言之,必须创建Object类
型变量,将自定义对象赋给该变量,然后传递该变量。
例子:
public class
DemoInterLocked
{
Object _x;
public Object
X
{
set
{
Object ovalue = value;
InterLocked.CompareExchange(ref _x , ovalue ,
null);
}
get
{
return _x;
}
}
}
//下面用Interlocked实现互斥的例子
using
System ;
using
System.Threading;
namespace
DemoSyncResource
{
class Resource
{
ReaderWriterLock rwl = new
ReaderWriterLock();
public void Read(Int32
threadNum)
{
rwl.AcquireReaderLock(Timeout.Infinite);
try
{
Console.WriteLine("开始读资源(Thread={0})",
threadNum);
Thread.Sleep(250);
Console.WriteLine("读取资源结束(Thread={0})",
threadNum);
}
finally
{
rwl.ReleaseReaderLock();
}
}
public void Write(Int32
threadNum)
{
rwl.AcquireWriterLock(Timeout.Infinite);
try
{
Console.WriteLine("开始写资源 (Thread={0})",
threadNum);
Thread.Sleep(750);
Console.WriteLine("写资源结束 (Thread={0})",
threadNum);
}
finally
{
rwl.ReleaseWriterLock();
}
}
}
class App
{
//临界资源
static Int32
numAsyncOps = 4;
//同步对象
static
AutoResetEvent asyncAreDone = new
AutoResetEvent(false);
//临界资源
static Resource res
= new Resource();
public static void Main()
{
for (Int32 threadNum = 0 ;threadNum
< 4 ; threadNum )
{
ThreadPool.QueueUserWorkItem(new
WaitCallback(UpdateResource),threadNum);
}
asyncAreDone.WaitOne();
Console.WriteLine("所有操作都结束");
}
static void UpdateResource(object state)
{
Int32 threadNum =
(Int32)state;
if ((threadNum %
2)!=0)
res.Read(threadNum);
else
res.Write(threadNum);
//利用Interlocked.Decrement互斥的修改临界资源
//每执行一个线程都将numAsyncOps减1
//如果numAsyncOps变为0,说明4个线程都执行完了
if(Interlocked.Decrement(ref numAsyncOps) ==
0)
asyncAreDone.Set();
}
}
}
上下文同步:(使用SynchronizationAttribute类)
该类属于
命名空间System.EnterpriseServices
使用该类可以对对象进行简单的自动同步。.net把同步锁和对象自动关
联,调用方法前锁定对象,方法返回后释放对象。
其不自动处理静态对象和方法。
使用
System.EnterpriseAttribute编程的时候,使用该类十分方便。因为一个上下文中对象由COM+运行库组合在一起。
使用方法:
[SynchronizationAttribute
(SynchronizationOption.Required)]
public class
myclass{......}
静态变量和方法同步:(使用ThreadStaticAttribute类)
静态变
量只能有一个实例。如果同步一个静态变量或方法,锁就要应用于整个类上,其结果会使得其他对象不能使用此类的静态变量
带有
ThreadStaticAttribute的静态变量不会在不同线程之间共享,每个访问的线程都有静态变量或方法的一个副本。如果一个线程修改了变量,
另一个线程不能看到这些变化。
使用方法:
[System.ThreadStaticAttribute()]
public static xxx(){}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。