归结一下子:C#线程同步的几种步骤

来源:互联网 发布:文明太空 涨潮 mac 编辑:程序博客网 时间:2024/04/28 20:38

我们在编程的时分,有时会运用多线程来解决问题,例如你的程序急需在后台老板处置一大堆数据,但还要施用户界面居于可操作状态;或许你的程序亟需访问一些外部资源悉数据库或网络资料等。这些景况你都可以创设一个头线程去处置,然则,多线程不可逆转地会带回一个问题,乃是线程同步的问题。如其这个问题处置糟糕,我们就会失去一些非预期的结果。

  在网上也看过一些至于线程同步的稿件,实则线程同步有好几种步骤,下部我就容易的做一下子归结。

  1、volatile关键字

  volatile是最容易的一种同步步骤,当然容易是要付出代价的。它只好在变量一级做同步,volatile的含意乃是告诉微处理器, 不用将我放入工作内存储器, 请直接在主存操作我。(【转自www.bitsCN.com 】)因而,应多线程与此同时访问该变量时,都将直接操作主存,从本质上作到了变量共享。

  能够被标识为volatile的务必是以次几类别型:(摘自MSDN)

•Any reference type.
•Any pointer type (in an unsafe context).
•The types sbyte, byte, short, ushort, int, uint, char, float, bool.
•An enum type with an enum base type of byte, sbyte, short, ushort, int, or uint.
  如:

Code

public class A
{
private volatile int _i;
public int I
{
get { return _i; }
set { _i = value; }
}
}
  但volatile并不能实现真个的同步,由于它的操作级别只停留在变量级别,而不是原子级别。如果是在单微处理器系统中,是没任何问题的,变量在主存中没有机会被其他人批改,由于唯有一个微处理器,这就叫做processor Self-Consistency。但在多处理器系统中,或者就会有问题。 每个微处理器都有自个儿的data cach,并且被更新的数据也未见得会立马写回来主存。之所以可能会促成不同步,但这种景况很难发作,由于cach的读写速度对等快,flush的频次也对等高,唯有在压力测试的时分才有可能发生,并且几率十分十分小。

  2、lock关键字

  lock是一种比较好用的容易的线程同步模式,它是透过为给定对象获取互斥锁来兑现同步的。它可以保证书当一个线程在要害代码段的时分,另一个线程不会进去,它唯其如此等候,待到那个线程对象被开释,且不说线程出了临界区。用法:   lock的参数务必是基于引述门类的对象,不用是根本部类像bool,int什么的,这么基本不能同步,原因是lock的参数要求是对象,如若传到int,准定要发作装箱操作,这么历次lock的都将是一个新的不同的对象。最好避免应用public部类或不受程序控制的对象范例,由于这么很可能以致死锁。特别是不用应用字符串作为lock的参数,由于字符串被CLR“暂留”,就是说整个应用程序中给定的字符串都惟独一个范例,因而更简略促成死锁现象。提议应用不被“暂留”的私有或受保护成员作为参数。实则某些种已经提供了专诚用来被锁的成员,例如Array门类提供SyncRoot,好多其它聚合门类也都提供了SyncRoot。



  之所以,运用lock应当注意以下几点: 

  一、如其一个种的范例是public的,最好不用lock(this)。由于施用你的种的人可能不知道你用了lock,如若他new了一个范例,而且对这个范例上锁,就很简略促成死锁。

  二、如若MyType是public的,不用lock(typeof(MyType))

  三、永远也不用lock一个字符串

  3、System.Threading.Interlocked

  关于整数数据部类的简略操作,可以用 Interlocked 种的成员来兑现线程同步,存在于System.Threading定名空间。Interlocked种有以次步骤:Increment , Decrement , Exchange 和CompareExchange 。应用Increment 和Decrement 可以军令状对一个整数的加减为一个原子操作。Exchange 步骤自动互换指定变量的值。CompareExchange 步骤结合了两个操作:比较两个值以及依据比较的结果将第三个值储存在内中一个变量中。比较和互换操作也是按原子操作实施的。如:

Code

int i = 零 ;
System.Threading.Interlocked.Increment( ref i);
Console.WriteLine(i);
System.Threading.Interlocked.Decrement( ref i);
Console.WriteLine(i);
System.Threading.Interlocked.Exchange( ref i, 100 );
Console.WriteLine(i);
System.Threading.Interlocked.CompareExchange( ref i, 十 , 100 );
Output:



  4、Monitor

  Monitor种提供了与lock相仿的效能,不过与lock不同的是,它能更好的统制同步块,应调用了Monitor的Enter(Object o)步骤时,能获取o的独占权,直到调用Exit(Object o)步骤时,才会开释对o的独占权,可以屡次调用Enter(Object o)步骤,只需要调用一样次数的Exit(Object o)步骤即可,Monitor类与此同时提供了TryEnter(Object o,[int])的一个满载步骤,该步骤尝试获取o对象的独占权,应获取独占权失败时,将回来false。

  但应用 lock 正常比直接运用 Monitor 更可取,一方面是由于 lock 更精简,另一方面是由于 lock 准保了即或受保护的代码挑动异常,也可以开释基础监视器。这是透过 finally 中调用Exit来兑现的。实质上,lock 便是用 Monitor 种来兑现的。下头两段代码是等效的:

Code

lock (x)
{
DoSomething();
}
等效于

object obj = ( object )x;
System.Threading.Monitor.Enter(obj);
try
{
DoSomething();
}
finally
{
System.Threading.Monitor.Exit(obj);
}


至于用法,请参照下边的代码:

Code

private static object m_monitorObject = new object ();
[STAThread]
static void Main( string [] args)
{
Thread thread = new Thread( new ThreadStart(Do));
thread.Name = " Thread一 " ;
Thread thread二 = new Thread( new ThreadStart(Do));
thread二.Name = " Thread二 " ;
thread.Start();
thread二.Start();
thread.Join();
thread二.Join();
Console.Read();
}
static void Do()
{
if ( ! Monitor.TryEnter(m_monitorObject))
{
Console.WriteLine( " Can't visit Object " + Thread.CurrentThread.Name);
return ;
}
try
{
Monitor.Enter(m_monitorObject);
Console.WriteLine( " Enter Monitor " + Thread.CurrentThread.Name);
Thread.Sleep( 5000 );
}
finally
{
Monitor.Exit(m_monitorObject);
}
}
  应线程一获取了m_monitorObject对象独占权时,线程二尝试调用TryEnter(m_monitorObject),此时此刻会因为没法获取独占权而回到false,输出信息如次:


  此外,Monitor还提供了三个静态步骤Monitor.Pulse(Object o),Monitor.PulseAll(Object o)和Monitor.Wait(Object o ) ,用于兑现一种唤醒机制的同步。对于这三个步骤的用法,可以参照MSDN,这边就不胪陈了。

  5、Mutex

  在运用上,Mutex与如上的Monitor比较贴近,不过Mutex不具备Wait,Pulse,PulseAll的效能,因而,我们不能施用Mutex兑现相仿的唤醒的效能。不过Mutex有一个比较大的特征,Mutex是跨过程的,因而我们可以在同一台机器甚或远道的机器上的多个过程上运用同一个互斥体。诚然Mutex也可以兑现历程内的线程同步,并且效能也更强大,但这种情况下,仍是推荐应用Monitor,由于Mutex类是win32打包的,之所以它所急需的互操作变换更耗资源。

  6、ReaderWriterLock

  在思考资源访问的时分,惯性上我们会对资源执行lock机制,但是在某些情况下,我们单单亟需读取资源的数据,而不是批改资源的数据,在这种情况下获取资源的独占权无疑会影响运作效率,因而.Net提供了一种机制,应用ReaderWriterLock进展资源访问时,如果在某1时刻资源并没有获取写的独占权,那么可以取得多个读的访问权,单个写下的独占权,如若某1时刻已经获取了写下的独占权,那么其它读取的访问权务必开展等候,参照以次代码:

Code

private static ReaderWriterLock m_readerWriterLock = new ReaderWriterLock();
private static int m_int = 零;
[STAThread]
static void Main(string[] args)
{
Thread readThread = new Thread(new ThreadStart(Read));
readThread.Name = "ReadThread一";
Thread readThread二 = new Thread(new ThreadStart(Read));
readThread二.Name = "ReadThread二";
Thread writeThread = new Thread(new ThreadStart(Writer));
writeThread.Name = "WriterThread";
readThread.Start();
readThread二.Start();
writeThread.Start();
readThread.Join();
readThread二.Join();
writeThread.Join();

Console.ReadLine();
}
private static void Read()
{
while (true)
{
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireReaderLock");
m_readerWriterLock.AcquireReaderLock(10000);
Console.WriteLine(String.Format("ThreadName : {零} m_int : {一}", Thread.CurrentThread.Name, m_int));
m_readerWriterLock.ReleaseReaderLock();
}
}

private static void Writer()
{
while (true)
{
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireWriterLock");
m_readerWriterLock.AcquireWriterLock(1000);
Interlocked.Increment(ref m_int);
Thread.Sleep(5000);
m_readerWriterLock.ReleaseWriterLock();
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " ReleaseWriterLock");
}
}
在程序中,我们起步两个线程获取m_int的读取访问权,运用一个线程获取m_int的写下独占权,施行代码后,输出如次:



可以看到,应WriterThread获取到写下独占权后,任万般它读取的线程都必须等候,直到WriterThread开释掉写下独占权后,才略获取到数据的访问权,应当注意的是,如上打印信息很明显显示出,可以多个线程与此同时获取数据的读取权,这从ReadThread一和ReadThread二的信息交互输出可以看出。

  7、SynchronizationAttribute

  当我们确定某个种的范例在同1时刻不得不被一个线程访问时,我们可以直接将类标识成Synchronization的,这么,CLR能自动对这个类执行同步机制,事实上,这边面涉及到同步域的概念,应类按如次设计时,我们可以保准种的范例没法被多个线程与此同时访问
  一). 在种的宣言中,增添System.Runtime.Remoting.Contexts.SynchronizationAttribute属性。
二). 承袭至System.ContextBoundObject
亟需注意的是,要兑现如上机制,种务必沿袭至System.ContextBoundObject,换言之,种务须是前后文绑定的。
一个示例类代码如次:

Code

[System.Runtime.Remoting.Contexts.Synchronization]
public class SynchronizedClass : System.ContextBoundObject
{

}


  8、MethodImplAttribute

  如若临界区是超越整个步骤的,且不说,整个步骤内部的代码都急需上锁的话,应用MethodImplAttribute属性会更简略一些。这么就不要在步骤内部加锁了,只需要在步骤上边加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,MehthodImpl和MethodImplOptions都在定名空间System.Runtime.CompilerServices 里边。但要注意这个属性会使整个步骤加锁,直到步骤回到,才开释锁。因而,运用上不太灵便。如其要提早开释锁,则应当应用Monitor或lock。我们来看一个例证:

Code

[MethodImpl(MethodImplOptions.Synchronized)]
public void DoSomeWorkSync()
{
Console.WriteLine( " DoSomeWorkSync() -- Lock held by Thread " +
Thread.CurrentThread.GetHashCode());
Thread.Sleep( 1000 );
Console.WriteLine( " DoSomeWorkSync() -- Lock released by Thread " +
Thread.CurrentThread.GetHashCode());
}
public void DoSomeWorkNoSync()
{
Console.WriteLine( " DoSomeWorkNoSync() -- Entered Thread is " +
Thread.CurrentThread.GetHashCode());
Thread.Sleep( 1000 );
Console.WriteLine( " DoSomeWorkNoSync() -- Leaving Thread is " +
Thread.CurrentThread.GetHashCode());
}

[STAThread]
static void Main( string [] args)
{
MethodImplAttr testObj = new MethodImplAttr();
Thread t一 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
Thread t二 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
t一.Start();
t二.Start();
Thread t三 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
Thread t四 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
t三.Start();
t四.Start();

Console.ReadLine();
}
这边,我们有两个步骤,我们可以对照一下子,一个是加了属性MethodImpl的DoSomeWorkSync(),一个是没加的DoSomeWorkNoSync()。在步骤中Sleep(1000)是为了在第一个线程还在步骤中时,第二个线程能够有足够的时间进去。对每个步骤诀别起了两个线程,我们先来看一下结果:



可以看出,关于线程一和二,也便是调用没加属性的步骤的线程,应线程二进来步骤后,还没有离开,线程一有进入了,那末,步骤没同步。我们再来见见线程三和四,应线程三进去后,步骤被锁,直到线程三开释了锁之后,线程四才进入。

  9、同步事件和等候句柄

  用lock和Monitor可以良好地起到线程同步的功用,但它们没法兑现线程其间传送事件。如若要兑现线程同步的与此同时,线程其间还要有交互,快要用到同步事件。同步事件是有两个状态(停止和非停止)的对象,它可以用于激活和挂起线程。

  同步事件有两种:AutoResetEvent和 ManualResetEvent。它们其间独一不同的地方乃是在激活线程以后,状态是不是自动由停止成为非停止。AutoResetEvent自动成为非停止,就是说一个AutoResetEvent只得激活一个线程。而ManualResetEvent要待到它的Reset步骤被调用,状态才变成非停止,在这先头,ManualResetEvent可以激活随便多个线程。

  可以调用WaitOne、WaitAny或WaitAll来使线程等候事件。它们其间的差别可以查看MSDN。应调用事件的 Set步骤时,事件将变成停止状态,等候的线程被唤醒。

  来看一个例证,这个例证是MSDN上的。由于事件只用以一个线程的激活,之所以施用 AutoResetEvent 或 ManualResetEvent 种都可以。

Code

static AutoResetEvent autoEvent;

static void DoWork()
{
Console.WriteLine(" worker thread started, now waiting on event");
autoEvent.WaitOne();
Console.WriteLine(" worker thread reactivated, now exiting");
}

[STAThread]
static void Main(string[] args)
{
autoEvent = new AutoResetEvent(false);

Console.WriteLine("main thread starting worker thread");
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();

Console.WriteLine("main thrad sleeping for 一 second");
Thread.Sleep(1000);

Console.WriteLine("main thread signaling worker thread");
autoEvent.Set();

Console.ReadLine();
}
我们先来看一下输出:



在主函数中,第一创办一个AutoResetEvent的范例,参数false示意初步状态为非停止,如果是true的话,初步状态则为停止。其后创造并起步一个头线程,在子线程中,透过调用AutoResetEvent的WaitOne步骤,使子线程等候指定事件的发作。其后主线程等候1秒后,调用AutoResetEvent的Set步骤,使状态由非停止成为停止,从新激活子线程。

参照:

一/MSDN(http://msdn.microsoft.com/zh-cn/library/ms173179(VS.80).aspx )

二/http://www.cnblogs.com/VincentWP/archive/2008/06/25/1229104.html

Code

public void Function()
{
object lockThis = new object ();
lock (lockThis)
{
// Access thread-sensitive resources.
}
}
本文来源:
我的异常网
Java Exception
Dotnet Exception
Oracle Exception

  • 1795 - ORA-00604: error occurred at recursive SQL level 1
  • 1796 - System.Web.Services.Protocols.SoapException: 服务器无法处理请求
  • 1797 - Visual Studio.net 2003 final beta English安装后Visual Studio.net的帮助的目录和索引消失
  • 1798 - STRUTS报java.lang.NullPointerException异常
  • 1799 - Struts validator框架与 html:errors标签的问题
  • 1800 - java.io.CharConversionException: isHexDigit
  • 1801 - 用户输入密码三次无效
  • 1802 - SqlException #230
  • 1803 - struts 中捕获 HTTP Status 404异常并重定向到首页
  • 1804 - java.lang.StackOverflowError
  • 1805 - Set cannot access Windows Installer component
  • 1806 - 应用程序正常初始化(oxc00000006)失败
  • 1807 - a different object with the same identifier value was already associated with the session
  • 1808 - java.lang.OutOfMemoryError:unable to create new native thread
  • 1809 - javax.servlet.ServletException: BeanUtils.populate
  • 1810 - 请检查下列特定分析错误详细信息并适当地修改源文件
  • 1811 - 调用的目标发生异常
  • 1812 - 在分析向此请求提供服务所需资源时出错
  • 1813 - 未处理的System.OutOfMemoryException类型的异常出现在 system.windows.forms.dll 中
  • 1814 - aspnet_wp.exe could not be launched because the username and or password supplied in the processModel section of the config file are invalid
  • 1815 - Visual Studio .NET Enterprise Architect - CHS: [2] ERROR processed; exception was thrown for retail build
  • 1816 - System.Data.OleDb.OleDbException: 找不到可安装的 ISAM
  • 原创粉丝点击
    热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 在成都乘出租车房产证掉了怎么办 电信不限量卡限网速了怎么办 only实体店换货没有小票怎么办 运管所包车单不给盖章怎么办 上海留学生落户过了取号时间怎么办 电信固话怎么办才能显示自己公司 日本跟团签证没有资产证明怎么办 摇号手机号换了密码忘了怎么办 小客车摇号的账号密码忘了怎么办 去银行交违章罚款单子丢了怎么办 我的身份证被别人办了信用卡怎么办 北京驾驶证在深圳扣了分怎么办 c照一次被扣12分怎么办 教师资格证认定申请表填错了怎么办 外地车在北京违章没有牡丹卡怎么办 护士辞职了原单位不给延续怎么办 护士证注册的单位倒闭了怎么办 护士证注册的医院倒闭了怎么办 显示发货了但没物流信息怎么办 考科目三被别人举报了怎么办 科目一考了5没过怎么办 常州c1驾驶证满了12分怎么办 预约科目三成功后没交钱怎么办 预约成功后驾校不提交档案怎么办 c2刚满一年的驾照扣12分怎么办 怀孕6个月咳嗽很厉害怎么办 家门口有电线杆影响我建楼房怎么办 卡丢了不知道卡号怎么办 驾考网上预约用户被锁定了怎么办 人才中心拿出来的户口掉了怎么办 父母是南京集体户孩子没户口怎么办 二建挂靠中介单位不给证怎么办 小包工头遇到工人在工地摔伤怎么办 外地人买了城中村的房子改造怎么办 深圳社保怀孕了产检异地怎么办 成都公租房租满6年怎么办 二建审核资料如果照片丢失怎么办 身份证被冒用在外地办社保怎么办 蔷薇的嫩叶都被太阳晒死了怎么办 乐视手机进水了屏幕失灵怎么办 乐视手机进水了屏幕不显示怎么办