【问题标题】:How to raise a static event thread safe in C#?如何在 C# 中引发静态事件线程安全?
【发布时间】:2012-05-05 21:10:09
【问题描述】:

我可以安全地引发静态事件线程吗? (我不完全确定这甚至意味着什么。) 整个事情是音频应用程序的一部分,所以我(至少)有音频处理线程和 UI 线程。

从处理音频和其他消息的非托管 C 代码调用 RaisePrintEvent。引发事件的调用可以从音频处理线程和 UI 线程进行。

public static event ProcessPrint Print = delegate {};

private static void RaisePrintEvent(string e)
{
    Print(e);
}

如何确保事件调用是同步的?或者可能只是在主线程中调用..

编辑:在此处阅读a smart solution

【问题讨论】:

    标签: c# multithreading events


    【解决方案1】:

    您必须确保所有 UI 操作都在主/UI 线程上执行。

    最好的方法是将消息从音频线程排队到某种形式的队列中,然后从主 UI 线程(例如 Forms.Timer 事件)中取出并显示它。

    Invoke() 不会让你走得太远,因为你在音频处理中的时间会被打破。如果你尝试它,并且你正在用你的 GUI 做一些事情,你可能会在音频中抽搐,因为 Invoke()-ing 其他线程上的某些东西会阻塞,直到其他线程处理用于执行 Invoke 的窗口消息'在引擎盖下'。

    如果您不了解上述部分或全部内容,请尝试使用消息队列。

    【讨论】:

    • @spender 没有别的办法,去过那里:)
    • 您认为使用 SynchronizationContext 是正确的方法吗? on 可以设置一个同步上下文,并且事件将在这个上下文中被调用。如果它为空,则无需调用..
    • 老实说,我对此了解不多。除非您在消息循环上使用 Post-ing,否则它不会为您提供任何帮助,这再次意味着您将排队等待,等等。
    【解决方案2】:

    您需要将处理程序复制到局部变量,以避免在您调用它时另一个线程取消订阅该事件时出现竞争条件(有关详细信息,请参阅here):

    private static void RaisePrintEvent(string e)
    {
        var handler = Print;
        if (handler != null)
        {
            handler(e);
        }
    }
    

    如果您希望事件一次只由一个线程引发,只需将调用锁定:

    private static readonly object _printLock = new object();
    private static void RaisePrintEvent(string e)
    {
        lock(_printLock)
        {
            var handler = Print;
            if (handler != null)
            {
                handler(e);
            }
        }
    }
    

    【讨论】:

    • k,竞争条件也可以通过添加一个简单的处理程序来修复,请参阅编辑。
    • @thalm,是的,但我真的不喜欢这种方法……对我来说这感觉像是一种肮脏的黑客行为;)
    • 好吧,这是个人喜好问题。但是在使用锁时,音频线程可能必须等到 gui 线程事件处理完毕......但是是的,这可能是一个解决方案。
    猜你喜欢
    • 1970-01-01
    • 2023-03-15
    • 2013-12-10
    • 2010-11-05
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 2014-09-16
    • 1970-01-01
    相关资源
    最近更新 更多