配置类型数据:静态全局变量还是传递它?

发布于 2024-08-16 08:06:21 字数 552 浏览 4 评论 0原文

因此,我正在库中编写一堆或多线程代码,这些代码将在后台运行。因此,我将 UI 传递到 SynchronizationContext 对象中,以便我可以将事件发布回主 UI 线程。

许多不同的对象都有 UI 可以订阅的公共事件。我最初只是将 SyncContext 对象作为参数添加到创建调用中,以初始化我的库。一旦传入,我将其存储在静态类的静态变量中,从而使其可以全局访问(尽管它是内部的)。

我喜欢这个,因为每当我需要在 UI 线程上运行一些代码时,我都可以轻松地做到这一点,而无需修改太多代码。但这意味着我的很多代码都对静态类有不明确的依赖。使依赖关系显式需要进行大量代码更改,才能将其添加到使用该变量的任何类的构造函数中,并将对它的引用存储在甚至创建需要它的对象的任何类中。

所以我有选项

A)静态变量隐藏我的SynchronizationContext的依赖关系

B)几乎每个类都知道并传递我的SynchronizationContext。

还有其他模式更适合这种情况吗?

.net框架3.5。主要 UI 将是 WPF,但我们希望它也能与 winforms 一起使用。

So I'm writing a bunch or multithreaded code in library that will run in the background. So I'm having the UI pass in SynchronizationContext object so I can Post events back to the main UI thread.

Many different objects have public events the UI can subscribe to. I initially just add the SyncContext object as a parameter to the creation call the sort of initializing my library. Once passed in I store it in a static variable on a static class thus making it globally accessible (although it is internal).

I like this because anytime I need to make some bit of code run on the UI thread I can easily do it without modifing much code. But it means that alot of my code has a dependancy on the Static class that isn't explicit. Making the dependency explicit would require alot of code change to add it to the constructor of any class that uses the variable, storing a reference to it in any class that even creates objects that need it.

So i have option

A) static variable that hides dependencies for my SynchronizationContext

B) nearly every class knows about and passes around my SynchronizationContext.

Is there another pattern that would be better for this sort of situation?

.net framwork 3.5. The main UI is going to be WPF, but we want it to work with winforms as well.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

做个ˇ局外人 2024-08-23 08:06:21

如果您知道您的库只能从单个同步上下文中使用,那么我认为您没有理由不能使用静态成员(您的选项 A)捕获同步上下文。

为什么您认为需要使这种依赖关系变得“显式”?

如果您的库可能同时从多个同步上下文中使用,则选项 A 可能会出现问题。 (例如,多个 UI 窗口在各自的线程和消息泵上运行)。但这是相当不寻常的 - 大多数情况下您只有一个 UI 线程和一个同步上下文。

此外,如果应用程序希望处理某些事件而不强制序列化到 UI 线程(通过同步上下文),则它无法这样做。

如果这些问题对您的图书馆来说不是问题,那么选项 A 应该是可行的。

编辑 - 回应评论:

乔尔,我不确定这是否有帮助,考虑到你所描述的内容,但如果你有时想要的话,你可能需要考虑一件事使用显式同步上下文就是使用线程本地存储来存储参数,而不需要创建方法的重写来获取参数。

例如,我过去需要允许 API 的调用者既使用当前调用线程的默认同步上下文,又需要显式设置不同的同步上下文。我可以采取的一种方法是提供允许传递同步上下文参数的 API 方法的重载。就我而言,这将非常难看,因为这意味着为相当不频繁的用例创建大量方法重载。

因此,我所做的是创建一个类来处理创建一个“范围”,在此期间当前同步上下文被覆盖(从而有效地将其传递给被调用的方法)。该类负责(按顺序)1)缓存当前同步上下文,2)将当前上下文设置为调用者指定的同步上下文,以及3)在“范围”末尾重置同步上下文。

它看起来像这样:

public class SyncContextScope : IDisposable
{
    SynchronizationContext m_contextOnEntry;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="useContext"></param>
    public SyncContextScope(SynchronizationContext useContext)
    {
        m_contextOnEntry = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(useContext);
    }

    #region IDisposable Members

    void IDisposable.Dispose()
    {
        SynchronizationContext.SetSynchronizationContext(m_contextOnEntry);
    }

    #endregion
}

您可以像这样使用它:

using(new SyncContextScope(yourSyncContext))
{
   yourApi.CallSomeMethod();
}

您的 API 方法(在上面的示例中为 CallSomeMethod)现在可以使用 SynchronizationContext.Current(它使用 TLS 来存储同步上下文)。所有这些都无需提供 sychronizationcontext 参数。

If you know that your library will only ever be used from a single synchronization context, then I see no reason why you can't capture the synchronization context using a static member (your option A).

Why do you see a need to make this dependency 'explicit'?

Where option A could potentially be problematic is if your library may be used from multiple synchronization contexts at the same time. (For example multiple UI windows running on their own thread and message pump). This is fairly unusual though - mostly you have a single UI thread and a single synchronization context.

Also, if an application wishes to handle some events without forcing serialization to the UI thread (via the sync context) it cannot do so.

If these issues are not a problem for your library, then option A should be viable.

Edit - in response to comment:

Joel, I'm not sure if this is helpful given what you describe, but one thing you might want to consider if you sometimes want to use an explicit synchronization context would be to use thread-local storage to store the parameter without needing to create overrides of the method to take the parameter.

For example, I have in the past myself needed to allow callers to my APIs to both use the default synchronization context for the current calling thread but also to explicitly set a different synchronization context. One approach I could have taken would have been to provide overloads of my API methods that allow passing a synchronization context parameter. In my case, this would have been pretty ugly since it would have meant creating a lot of method overloads for a fairly infrequent use case.

So, what I did instead was create a class that handled creating a 'scope' during which the current sync context gets overridden (thereby effectively passing it to the called methods). The class takes care of (in order) 1) caching the current sync context, 2) setting the current context to a sync context specified by the caller, and 3) resetting the sync context at the end of the 'scope'.

Here's what it looks like:

public class SyncContextScope : IDisposable
{
    SynchronizationContext m_contextOnEntry;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="useContext"></param>
    public SyncContextScope(SynchronizationContext useContext)
    {
        m_contextOnEntry = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(useContext);
    }

    #region IDisposable Members

    void IDisposable.Dispose()
    {
        SynchronizationContext.SetSynchronizationContext(m_contextOnEntry);
    }

    #endregion
}

And you use it like so:

using(new SyncContextScope(yourSyncContext))
{
   yourApi.CallSomeMethod();
}

Your API's method (in the above example CallSomeMethod) can now make use of the SynchronizationContext.Current (which uses TLS to store a sync context). All without having to resort to providing a sychronizationcontext parameter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文