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

一、提出结论

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

1、[MethodImplAttribute(MethodImplOptions.Synchronized)]仍然采用加锁的机制实现线程的同步。

2、如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到instance method,相当于对当前实例加锁。

3、如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到static method,相当于当前类型加锁

二、基于instance method的线程同步

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

class SyncHelper
{
    public void Execute()
    {
        Console.WriteLine("Excute at {0}", DateTime.Now);
        Thread.Sleep(5000);
    }
}

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

class Program
{
    static void Main(string[] args)
    {
        SyncHelper helper = new SyncHelper();
        Timer timer = new Timer(
        delegate
        {
            helper.Execute();
        }, null, 0, 1000);

Console.Read();

}
}

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

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

[MethodImpl(MethodImplOptions.Synchronized)]
public void Execute()
{
    Console.WriteLine("Excute at {0}", DateTime.Now);
    Thread.Sleep(5000);
}

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

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

public void LockMyself()
{
   lock (this)
    {
        Console.WriteLine("Lock myself at {0}", DateTime.Now);
        Thread.Sleep(5000);
        Console.WriteLine("Unlock myself at {0}", DateTime.Now);
    }
}

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

static void Main(string[] args)
{
    SyncHelper helper = new SyncHelper();

Thread thread = new Thread(
        delegate()
        {

helper.LockMyself();

});
    thread.Start();
    Timer timer = new Timer(
    delegate
    {
        helper.Execute();
    }, null, 0, 1000);

Console.Read();
}

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

三、基于static method的线程同步

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

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

//[MethodImpl(MethodImplOptions.Synchronized)]
public static void Execute()
{
    Console.WriteLine("Excute at {0}", DateTime.Now);
    Thread.Sleep(5000);
}

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

static void Main(string[] args)
{
    Timer timer = new Timer(
    delegate
    {
        SyncHelper.Execute();
    }, null, 0, 1000);

Console.Read();
}

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

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

[MethodImpl(MethodImplOptions.Synchronized)]
public static void Execute()
{
    Console.WriteLine("Excute at {0}", DateTime.Now);
    Thread.Sleep(5000);
}

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

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

public static void LockType()
{
    lock (typeof(SyncHelper))
    {
        Console.WriteLine("Lock SyncHelper type at {0}", DateTime.Now);
        Thread.Sleep(5000);
        Console.WriteLine("Unlock SyncHelper type at {0}", DateTime.Now);
    }
}

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

static void Main(string[] args)
{
    Thread thread = new Thread(
        delegate()
        {
            SyncHelper.LockType();
        });
    thread.Start();

Timer timer = new Timer(
    delegate
    {
        SyncHelper.Execute();
    }, null, 0, 1000);

Console.Read();
}

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

四、总结

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

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

出处:https://www.cnblogs.com/zhuawang/archive/2013/05/27/3102834.html

[MethodImpl(MethodImplOptions.Synchronized)]、lock(this)与lock(typeof(...))的更多相关文章

  1. C# Note26: [MethodImpl(MethodImplOptions.Synchronized)]与lock机制

    在进行.NET开发时,经常会遇见如何保持线程同步的情况.在众多的线程同步的可选方式中,加锁无疑是最为常用的.如果仅仅是基于方法级别的线程同步,使用System.Runtime.CompilerServ ...

  2. [MethodImpl(MethodImplOptions.Synchronized)]与lock机制

    [MethodImpl(MethodImplOptions.Synchronized)]与lock机制 在进行.NET开发时,经常会遇见如何保持线程同步的情况.在众多的线程同步的可选方式中,加锁无疑是 ...

  3. [MethodImpl(MethodImplOptions.Synchronized)]

    在NopCommerce项目的Nop.Core类库中有一个EngineContext类中有一个Initialize方法用到了[MethodImpl(MethodImplOptions.Synchron ...

  4. C#方法同步 [MethodImpl(MethodImplOptions.Synchronized)]

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. MethodImplOptions.Synchronized的一点讨论

    Review代码发现有一个方法加了[MethodImpl(MethodImplOptions.Synchronized)] 属性,这个属性的目的,从名字上就可以看出,是要对所有线程进行同步执行. 对方 ...

  6. synchronized 的局限性 与 Lock 的优点

    我们已经知道,synchronized 是Java的关键字,是Java的内置特性,在JVM层面实现了对临界资源的同步互斥访问,但 synchronized 粒度有些大,在处理实际问题时存在诸多局限性, ...

  7. Synchronized和Lock, 以及自旋锁 Spin Lock, Ticket Spin Lock, MCS Spin Lock, CLH Spin Lock

    Synchronized和Lock synchronized是一个关键字, Lock是一个接口, 对应有多种实现. 使用synchronized进行同步和使用Lock进行同步的区别 使用synchro ...

  8. 警惕32位程序在MethodImplOptions.Synchronized在x64机器上的同步缺陷[z]

    https://www.cnblogs.com/junchu25/archive/2012/08/10/2631422.html 上周四产品上线一切运行正常,做了一点小改动后周四晚上发布,周五大量用户 ...

  9. mysql 有报错  ERROR! MySQL is not running, but lock file (/var/lock/subsys/mysql) exists

    sh-4.1# /etc/init.d/mysqld status ERROR! MySQL is not running, but lock file (/var/lock/subsys/mysql ...

随机推荐

  1. 树莓派安装配置teamviewer host

    过程: 下载teamviewer,直接到teamviewer的官网上直接下载 下载完后,安装teamviewerw sudo dpkg -i 下载的文件的路径+文件名 安装完后会提示存在依赖问题,修复 ...

  2. 30 年前的圣诞节,Python 序章被谱写

    1989 年圣诞节期间,已经从阿姆斯特丹大学(University of Amsterdam)获得数学和计算机硕士学位的 Guido van Rossum,为了打发圣诞节的无趣,决心开发一个新语言解释 ...

  3. join当前线程等待指定的线程结束后才能继续运行

    模拟一个QQ游戏大厅斗地主 /** sleep(休眠.睡眠) join当前线程等待指定的线程结束后才能继续运行 */ class Player extends Thread{ private Stri ...

  4. 在vue中如何引jquer

    最近学习vue,习惯性的通过<script>标签引入jquery,写完后报错才想起来,这种方式在vue是不适用的. 1:因为已经安装了vue脚手架,所以需要在webpack中全局引入jqu ...

  5. VMware 虚拟化编程(3) —VMware vSphere Web Service API 解析

    目录 目录 前文列表 VMware vSphere Web Services API VMware vSphere Web Services SDK vSphere WS API 中的托管对象 Man ...

  6. value_counts()函数

    value_counts函数用于统计dataframe或series中不同数或字符串出现的次数 ascending=True时,按升序排列. normalize=True时,可计算出不同字符出现的频率 ...

  7. centos 7 lnmp环境编译安装zabbix-3.4.14

    一.安装环境(zabbix3.0需要php在5.5版本以上) [root@localhost ~]# cat /etc/redhat-release CentOS Linux release (Cor ...

  8. python中的包和文件夹的区别

    python的模块,就不得不说包(package),package是module的集合,在一个package中有很多的module, 还是以之前的index.py与baiduHq.py模块为案例,说明 ...

  9. 【BASIS系列】SAP 批量锁住用户和TCODE的方法

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[BASIS系列]SAP 批量锁住用户和TCOD ...

  10. Mac017--Jenkins 持续集成

    Jenkins 是一个开源项目,提供了一种易于使用的持续集成系统,使开发者从繁杂的集成中解脱出来,专注于更为重要的业务逻辑实现上.同时 Jenkins 能实施监控集成中存在的错误,提供详细的日志文件和 ...