前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[MethodImpl(MethodImplOptions.Synchronized)]、lock(this)与lock(typeof(...))

[MethodImpl(MethodImplOptions.Synchronized)]、lock(this)与lock(typeof(...))

作者头像
蒋金楠
发布2022-05-09 13:02:23
1.2K0
发布2022-05-09 13:02:23
举报
文章被收录于专栏:大内老A大内老A

对于稍微有点经验的.NET开发人员来说,倘若被问及如何保持线程同步,我想很多人都能说好好几种。在众多的线程同步的可选方式中,加锁无疑是最为常用的。如果仅仅是基于方法级别的线程同步,使用System.Runtime.CompilerServices.MethodImplAttribute无疑是最为简洁的一种方式。MethodImplAttribute可以用于instance method,也可以用于static method。当在某个方法上标注了MethodImplAttribute,并指定MethodImplOptions.Synchronized参数,可以确保在不同线程中运行的该方式以同步的方式运行。我们几天来讨论MethodImplAttribute(MethodImplOptions.Synchronized)和lock的关系。

一、提出结论

在进行讨论之前,我先提出下面3个结论:

  • [MethodImplAttribute(MethodImplOptions.Synchronized)]仍然采用加锁的机制实现线程的同步。
  • 如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到instance method,相当于对当前实例加锁。
  • 如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到static method,相当于当前类型加锁

二、基于instance method的线程同步

为了验证我们上面提出的结论,我作了一个小小的例子。在一个console application中定义了一个class:SyncHelper,其中定义了一个方法Execute。打印出方法执行的时间,并休眠当前线程模拟一个耗时的操作:

代码语言:javascript
复制
1: class SyncHelper   2: {   3:     public void Execute()   4:     {   5:         Console.WriteLine("Excute at {0}", DateTime.Now);   6:         Thread.Sleep(5000);   7:     }   8: }

在入口Main方法中,创建SyncHelper对象,通过一个System.Threading.Timer对象实现每隔1s调用该对象的Execute方法:

代码语言:javascript
复制
1: class Program   2: {   3:     static void Main(string[] args)   4:     {   5:         SyncHelper helper = new SyncHelper();   6:         Timer timer = new Timer(   7:         delegate   8:         {   9:             helper.Execute();  10:         }, null, 0, 1000);   11:    12:         Console.Read();   13:    14:     }  15: }   16:

由于Timer对象采用异步的方式进行调用,所以虽然Execute方法的执行时间是5s,但是该方法仍然是每隔1s被执行一次。这一点从最终执行的结果可以看出:

image
image

为了让同一个SyncHelper对象的Execute方法同步执行,我们在Execute方法上添加了如下一个MethodImplAttribute:

代码语言:javascript
复制
1: [MethodImpl(MethodImplOptions.Synchronized)]   2: public void Execute()   3: {   4:     Console.WriteLine("Excute at {0}", DateTime.Now);   5:     Thread.Sleep(5000);   6: }

从如下的输出结果我们可以看出Execute方法是以同步的方式执行的,因为两次执行的间隔正式Execute方法执行的时间:

image
image

在一开始我们提出的结论中,我们提到“如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到instance method,相当于对当前实例加锁”。说得直白一点:[MethodImplAttribute(MethodImplOptions.Synchronized)] = lock(this)。我们可以通过下面的实验验证这一点。为此,在SyncHelper中定义了一个方法LockMyself。在此方法中对自身加锁,并持续5s中,并答应加锁和解锁的时间。

代码语言:javascript
复制
1: public void LockMyself()   2: {   3:    lock (this)   4:     {   5:         Console.WriteLine("Lock myself at {0}", DateTime.Now);   6:         Thread.Sleep(5000);   7:         Console.WriteLine("Unlock myself at {0}", DateTime.Now);   8:     }   9: }

我们在Main()中以异步的方式(通过创建新的线程的方式)调用该方法:

代码语言:javascript
复制
1: static void Main(string[] args)   2: {   3:     SyncHelper helper = new SyncHelper();   4:     5:     Thread thread = new Thread(   6:         delegate()   7:         {               8:     9:              helper.LockMyself();  10:    11:         });  12:     thread.Start();  13:     Timer timer = new Timer(  14:     delegate  15:     {  16:         helper.Execute();  17:     }, null, 0, 1000);   18:    19:     Console.Read();  20: }

结合我们的第二个结论想想最终的输出会是如何。由于LockMyself方法是在另一个线程中执行,我们可以简单讲该方法的执行和Execute的第一个次执行看作是同时的。但是MethodImplAttribute(MethodImplOptions.Synchronized)]果真是通过lock(this)的方式实现的话,Execute必须在等待LockMyself方法执行结束将对自身的锁释放后才能得以执行。也就是说LockMyself和第一次Execute方法的执行应该相差5s。而输出的结果证实了这点:

image
image

三、基于static method的线程同步

讨论完再instance method上添加MethodImplAttribute(MethodImplOptions.Synchronized)]的情况,我们相同的方式来讨论倘若一样的MethodImplAttribute被应用到static方法,又会使怎样的结果。

我们先将Execute方法上的MethodImplAttribute注释掉,并将其改为static方法:

代码语言:javascript
复制
1: //[MethodImpl(MethodImplOptions.Synchronized)]   2: public static void Execute()   3: {   4:     Console.WriteLine("Excute at {0}", DateTime.Now);   5:     Thread.Sleep(5000);   6: }

在Main方法中,通过Timer调用该static方法:

代码语言:javascript
复制
1: static void Main(string[] args)   2: {   3:     Timer timer = new Timer(   4:     delegate   5:     {   6:         SyncHelper.Execute();   7:     }, null, 0, 1000);    8:     9:     Console.Read();  10: }

毫无疑问,Execute方法将以1s的间隔异步地执行,最终的输出结果如下:

image
image

然后我们将对[MethodImpl(MethodImplOptions.Synchronized)]的注释取消:

代码语言:javascript
复制
1: [MethodImpl(MethodImplOptions.Synchronized)]   2: public static void Execute()   3: {   4:     Console.WriteLine("Excute at {0}", DateTime.Now);   5:     Thread.Sleep(5000);   6: }

最终的输出结果证实了Execute将会按照我们期望的那样以同步的方式执行,执行的间隔正是方法执行的时间:

image
image

我们回顾一下第三个结论:“如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到static method,相当于当前类型加锁”。为了验证这个结论,在SyncHelper中添加了一个新的static方法:LockType。该方法对SyncHelper tpye加锁,并持续5s中,在加锁和解锁是打印出当前时间:

代码语言:javascript
复制
1: public static void LockType()   2: {   3:     lock (typeof(SyncHelper))   4:     {   5:         Console.WriteLine("Lock SyncHelper type at {0}", DateTime.Now);   6:         Thread.Sleep(5000);   7:         Console.WriteLine("Unlock SyncHelper type at {0}", DateTime.Now);   8:     }   9: }

在Main中,像验证instance method一样,创建新的线程执行LockType方法:

代码语言:javascript
复制
1: static void Main(string[] args)   2: {   3:     Thread thread = new Thread(   4:         delegate()   5:         {   6:             SyncHelper.LockType();   7:         });   8:     thread.Start();    9:    10:     Timer timer = new Timer(  11:     delegate  12:     {  13:         SyncHelper.Execute();  14:     }, null, 0, 1000);   15:    16:     Console.Read();  17: }   18:

如果基于static method的[MethodImplAttribute(MethodImplOptions.Synchronized)]是通过对Type进行加锁实现。那么通过Timer轮询的第一个Execute方法需要在LockType方法执行完成将对SyncHelper type的锁释放后才能执行。所以如果上述的结论成立,将会有下面的输出:

image
image

四、总结

对于加锁来说,锁的粒度的选择显得至关重要。在不同的场景中需要选择不同粒度的锁。如果选择错误往往会对性能造成很到的影响,严重时还会引起死锁。就拿[MethodImplAttribute(MethodImplOptions.Synchronized)]来说,如果开发人员对它的实现机制不了解,很有可能使它lock(this)或者lock(typeof(…))并存,造成方法得不到及时地执行。

最后说一句题外话,因为字符串驻留机制的存在,切忌对string进行加锁。

作者:Artech 出处:http://artech.cnblogs.com 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2008-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档