gpt4 book ai didi

c# - 流利的接口(interface)-确保新实例

转载 作者:行者123 更新时间:2023-12-03 14:44:24 25 4
gpt4 key购买 nike

我有一个公开流利的界面风格的类,我也想成为线程安全的。

目前,在类的实例上调用可链接方法会设置各种带有操作的集合(Func<T>)。

当要求结果时,实际工作就发生了。这允许用户以任何顺序链接方法调用,例如:

var result = myFluentThing
.Execute(() => serviceCall.ExecHttp(), 5)
.IfExecFails(() => DoSomeShizzle())
.Result<TheResultType>();

(此处,5是重试失败的服务调用的次数。)

显然,这不是线程安全的或可重入的。

有哪些常见的设计模式可以解决此问题?

如果必须先调用Execute方法,则每次都可以简单地返回该类的新实例以进行处理,但是由于可以在链中的任何位置调用任何方法,那么您将如何解决此问题?

我更想了解解决此问题的各种方法,而不是仅仅一个简单的“使它正确运行”的答案。

我将完整的代码放在GitHub上,以防任何人需要更广泛的上下文来实现我的目标: https://github.com/JamieDixon/ServiceManager

最佳答案

我们可以将流利的方法分为两种类型:变异和非变异。

在.NET中,突变案例不是很普遍(直到Linq引入了对流利方法的大量使用后,流利的方法才普遍使用,相比之下,Java在属性 setter 中大量使用它们,而C#却使用属性来提供相同的语法)将属性设置为设置字段)。不过,一个示例是StringBuilder

StringBuilder sb = new StringBuilder("a").Append("b").Append("c");

基本形式为:
TypeOfContainingClass SomeMethod(/*... arguments ... */)
{
//Do something, generally mutating the current object
//though we could perhaps mix in some non-mutating methods
//with the mutating methods a class like this uses, for
//consistency.
return this;
}

这是一种本质上非线程安全的方法,因为它会使有问题的对象发生变异,因此来自不同线程的两次调用将相互干扰。面对这样的调用,就一定可以创建一个线程安全的类,因为它不会处于不连贯的状态,但是通常,当我们采用这种方法时,我们会关心那些突变的结果,和那些。例如。在上面的 StringbBuilder示例中,我们关心 sb最终以字符串 "abc"结尾,线程安全的 StringBuilder毫无意义,因为我们不认为可以成功地接受 "abc""acb"最终被接受-这样的保证假设的类本身将是线程安全的,但调用代码则不是。

(这并不意味着我们不能在线程安全代码中使用此类;我们可以在线程安全代码中使用任何类,但这对我们没有帮助)。

现在,非变异形式本身就是线程安全的。这并不意味着所有使用都是线程安全的,而是意味着可以。考虑下面的LINQ代码:
var results = someSource
.Where(somePredicate)
.OrderBy(someOrderer)
.Select(someFactory);

只要是以下情况,这是线程安全的:
  • 遍历someSource是线程安全的。
  • 调用somePredicate是线程安全的。
  • 调用someOrder是线程安全的。
  • 调用someFactory是线程安全的。

  • 这看起来似乎有很多标准,但实际上,最后一个条件都是相同的:我们要求 Func实例具有功能性-它们没有副作用*,但是它们返回的结果取决于它们的输入(我们可以在保持线程安全的同时修改一些有关功能的规则,但现在不要让事情复杂化)。嗯,这大概就是他们想出名字 Func时所考虑的情况。请注意,Linq最常见的情况就是这种描述。例如。:
    var results = someSource
    .Where(item => item.IsActive)//functional. Thread-safe as long as accessing IsActive is.
    .OrderBy(item => item.Priority)//functional. Thread-safe as long as accessing Priority is.
    .Select(item => new {item.ID, item.Name});//functional. Thread-safe as long as accessing ID and Name is.

    现在,有了99%的属性实现,只要我们没有其他线程编写,从多个线程调用 getter就是线程安全的。这是常见的情况,因此就能够安全地满足这种情况而言,我们是线程安全的,尽管面对另一个执行此类突变的线程,我们也不是线程安全的。

    同样,我们可以将 someSource之类的来源分为四类:
  • 内存中的集合。
  • 对数据库或其他数据源的调用。
  • 一个可枚举的对象,它将对从某处获得的信息进行单次传递,但其中的源不具有在第二次迭代中再次检索该信息所需的信息。
  • 其他。

  • 第一种情况的绝大多数在其他读者面前是线程安全的。面对并发编写器,有些脚本也是线程安全的。
    对于第二种情况,它取决于实现-它是否根据当前线程的需要获取连接等,还是使用调用之间共享的连接?
    对于第三种情况,除非我们认为“丢失”另一个线程代替我们获得的那些项是可以接受的,否则它绝对不是线程安全的。
    而且,“其他”与“其他”一样。

    因此,从所有方面来看,我们没有任何东西可以保证线程安全,但是我们确实有一些东西可以为我们提供足够的线程安全性,因此,如果与其他组件配合使用,我们可以提供所需的线程安全性,得到它。

    面对所有可能的用途时100%线程安全?不,没有什么可以给你的。确实,没有数据类型是线程安全的,只有特定的一组操作-在将数据类型描述为“线程安全”时,我们说的是其所有成员方法和属性都是踩踏安全的,而在将方法或属性描述为线程安全时,我们就是说它本身是线程安全的,因此可以成为线程安全操作组的一部分,但并非每组线程安全操作都是线程安全的。

    如果要实现这种方法,则需要基于调用的对象(如果是成员而不是扩展名)和参数创建一个方法或扩展来创建一个对象,但是这些参数不会发生变化。

    让我们对 Enumerable.Select这样的方法进行两种单独的实现进行讨论:
    public static IEnumerable<TResult> SelectRightNow<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector)
    {
    var list = new List<TResult>();
    foreach(TSource item in source)
    list.Add(selector(item));
    return list;
    }

    public static IEnumerable<TResult> SelectEventually<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector)
    {
    foreach(TSource item in source)
    yield return selector(item);
    }

    在这两种情况下,该方法都会立即返回一个新对象,该对象以某种方式基于 source的内容。但是只有第二个具有我们从linq获得的 source的延迟迭代。实际上,第一种方法使我们比第二种方法更好地处理某些多线程情况,但是这样做很讨厌(如果您希望例如在持有锁的同时获取副本作为并发管理的一部分,则可以通过在持有锁的同时获取副本来做到这一点。锁定,而不是其他任何东西)。

    无论哪种情况,返回的对象都是我们可以提供的线程安全的关键。第一个已经获得了有关其结果的所有信息,因此,只要仅在本地对单个线程进行引用,就可以保证线程安全。第二个具有产生这些结果所需的信息,因此只要它仅在本地引用到单个线程,访问源是线程安全的,并且调用 Func是线程安全的,则它是线程安全的(并且那些也适用首先创建第一个)。

    总而言之,如果我们有一些方法产生的对象仅引用源代码和 Func,则可以像源代码和 Func一样保证线程安全,但没有哪一个更安全。

    *内存优化会带来从外部看不到的副作用。如果我们的 Func或他们调用的某种东西(例如,getter)使用了它,则必须以线程安全的方式来实现备忘录,以使线程安全成为可能。

    关于c# - 流利的接口(interface)-确保新实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12226089/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com