对于稍微有点经验的.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. (转)GitHub中PR(Pull request)操作 - 请求合并代码

    转:https://www.jianshu.com/p/b365c743ec8d 前言 本文尽量使用图形工具介绍如何向开源项目提交 Pull Request,一次亲身经历提交 PR 1.fork 项目 ...

  2. mui.ajax中文乱码

    估计这是个bug: //mui 的 ajax 中文乱码 var url = 'http://api.juheapi.com/japi/toh?key=1f26c107d8864bdfb98202bc3 ...

  3. P1080国王游戏

    传送 最大值最小什么的一看就是二分了qwq 然鹅并不知道怎么检查,所以我们换个思路 我们要求出最小的最大值,这肯定和大臣的排列有关,会不会有什么规律? 先看看只有两个大臣的情况 排列:1 2,ans1 ...

  4. seaborn

    Seaborn是基于matplotlib的Python数据可视化库. 它提供了一个高级界面,用于绘制引人入胜且内容丰富的统计图形. 一  风格及调色盘 风格 1 sns.set()  模式格式 2 s ...

  5. tensorflow和keras的安装

    1 卸载tensorflow方法,在终端输入:  把protobuf删除了才能卸载干净. sudo pip uninstall protobuf sudo pip uninstall tensorfl ...

  6. DataFrame 结构

    概念 DataFrame 是表格型的数据结构 ,DataFrame 本质上可以看做是由series 组成的字典, 它既有行索引,也有列索引.  它并不是列表,也不是字典,.

  7. 介绍一款代理端口管理工具--Proxfier

    官网下载地址: https://www.proxifier.com/download/ProxifierSetup.exe 用户名随意填注册码下边 5EZ8G-C3WL5-B56YG-SCXM9-6Q ...

  8. github创建仓库,往github上上传自己的项目

    k第一步: 在github上创建仓库 第二步: 创建一个新的项目,填写项目名称,描述 创建完成之后,跳转到下面的页面,下面红框中的网址要记住,在后面上传代码的时候需要使用 这个地址必须要记住!!! 第 ...

  9. vue项目 引入js文件

    例如我想将laydate.js引到vue项目中 将用到的js文件放到static文件夹内,在项目的根目录下的index.html内引入 <script src="static/js/l ...

  10. Oracle数据库SQL语句的分类

    1986年10月,美国国家标准协会对SQL进行规范后,以此作为关系式数据库管理系统的标准语言,1987年在国际标准组织的支持下成为国际标准.不过各种通行的数据库系统其实在实践过程中都对SQL规范的作了 ...