RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 598482
Accepted
rdorn
rdorn
Asked:2020-12-02 12:13:14 +0000 UTC2020-12-02 12:13:14 +0000 UTC 2020-12-02 12:13:14 +0000 UTC

事件执行

  • 772

在 ILSpy、Google 和一两句脏话的帮助下,我能够准确地重现方法Add和Remove标准的实现event。

结果,事件代码看起来是这样的,无论如何,在Release编译之后,IL代码是100%等价的。在 Debug 中,它自然有所不同,但只是略有不同,即在检查循环条件方面。

//field-like объявление события
public event Action SampleEvent1;

//итоги декомпиляции
private Action eventDelegate;
public event Action SampleEvent2
{
    add
    {
        Action current = eventDelegate, 
        Action comparer;
        do
        {
            comparer = current;
            Action combine = comparer + value;
            current = Interlocked.CompareExchange(ref eventDelegate, combine, comparer);
        }
        while (!object.ReferenceEquals(current, comparer));
    }
    remove
    {
        Action current = eventDelegate, 
        Action comparer;
        do
        {
            comparer = current;
            Action combine = comparer - value;
            current = Interlocked.CompareExchange(ref eventDelegate, combine, comparer);
        }
        while (!object.ReferenceEquals(current, comparer));
    }
}

现在给鉴赏家一个问题:我知道所有这些都是支持多线程所必需的,并且使用lock会导致死锁,但我不完全理解这个选项是如何工作的,最重要的是,为什么。

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    VladD
    2020-12-02T16:33:37Z2020-12-02T16:33:37Z

    看。

    订阅/取消订阅事件应该是原子的,这样就不可能有人订阅了而事件没有到达。旧版本确实阻止了:

    private EventHandler _myEvent;        
    public event EventHandler MyEvent
    {
        add { lock (this) _myEvent += value; }
        remove { lock (this) _myEvent -= value; }        
    }
    

    这种方式的缺点是锁需要对象,拿哪个对象呢?你可以采用一个“不可见”的对象,但是这个对象必须以某种方式在标准中指定并可供使用(例如,如果我们想在同一个锁下读取委托值),这不太好,因为它规定了实施细节。因此,它被使用this。

    但this反过来又导致了另一个问题:它可以被任何人从外面阻挡!因此,决定放弃这个想法,转而采用非阻塞(无锁)算法,这种算法不需要锁定对象,而且速度更快、效率更高。


    这个怎么运作?这就是如何。稍微重命名变量:

    Action eventDelegate;
    
    public void AddSampleEvent1(Action value)
    {
        Action current = eventDelegate;
        Action noncombined;
        do
        {
            noncombined = current;
            Action combined = (Action)Delegate.Combine(noncombined, value);
            current = Interlocked.CompareExchange(ref eventDelegate, combined, noncombined);
        }
        while (current != noncombined);
    }
    

    看看它做Interlocked.CompareExchange了什么,为了清楚起见,可以这样重写:

    Action eventDelegate;
    
    public void AddSampleEvent1(Action value)
    {
        Action current = eventDelegate;
        Action noncombined;
        do
        {
            noncombined = current;
            Action combined = (Action)Delegate.Combine(noncombined, value);
            atomic // фиктивное ключевое слово
            {
                if (noncombined == eventDelegate)
                    eventDelegate = combined;
                current = eventDelegate;
            }
        }
        while (current != noncombined);
    }
    

    发生了什么?在current循环迭代开始时,会有一个值eventDelegate。我们将它存储在一个临时变量中noncombined并将value其添加以获取委托combined。现在我们正在尝试将结果写回。如果此时没有人设法从另一个线程更改我们的委托(很可能会),那么它将Interlocked.CompareExchange成功完成,将委托写入到位,并且委托的current旧值将在其中。这将结束循环,检查current != noncombined将给出false.

    如果在我们尝试合并时,另一个线程发生了变化eventDelegate,则条件测试Interlocked.CompareExchange将失败。在这种情况下,eventDelegate什么都不能写入,因为我们将丢失更改后的值!然后我们只需将这个新值写入current并转到下一次迭代(测试current != noncombined将给出true)。在下一次迭代中,我们将做同样的事情:eventDelegate我们将尝试将一个新委托与当前值结合起来,并将其写入到位,同时检查eventDelegate在此期间是否没有人再次更改。这应该是一种典型的非阻塞技术,我在非阻塞算法中看到了很多类似的代码。

    • 8

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5