【问题标题】:Circular dependency with cross-cutting concern具有横切关注点的循环依赖
【发布时间】:2013-07-18 17:11:18
【问题描述】:

因此,此示例旨在尝试给出我正在尝试修改的更大系统(即 Orchard CMS)的简单视图。因此,它可能并不完美。


我正在尝试创建一个通过设置管理的日志记录系统。我遇到的问题是检索设置会导致发生日志记录。这是一个希望描述问题的简单示例:

static void Main(string[] args)
{
    string[] messages = "this is a test.  but it's going to be an issue!".Split(' ');

    Parallel.ForEach(messages, Log);

    Console.ReadLine();
}

public static void Log(string message)
{
    Console.WriteLine(GetPrefix() + message);
}

public static string GetPrefix()
{
    Log("Getting prefix!");
    return "Prefix: ";
}

这是一个明显的StackOverflowException。但是,我该如何解决呢?在收到GetPrefix 的回复之前,我不能只禁用日志记录,因为我可能会错过日志。 (事实上​​,在这个简单的例子中,除了第一个之外,我都错过了。)

static void Main(string[] args)
{
    string[] messages = "this is a test.  but it's going to be an issue!".Split(' ');

    Parallel.ForEach(messages, Log);

    Console.ReadLine();
}

static bool _disable = false;
public static void Log(string message)
{
    if (_disable)
    {
        return;
    }
    _disable = true;
    Console.WriteLine(GetPrefix() + message);
    _disable = false;
}

public static string GetPrefix()
{
    Log("Getting prefix!");
    return "Prefix: ";
}

(^不好。)

请注意,我目前无法控制 GetPrefix 方法,只能控制 Log 方法。

我不确定是否有办法解决这个问题;我可能需要将设置放在其他地方(例如配置或单独的设置文件)。但是,如果有人有想法或建议,我很乐意尝试任何事情,因为我更愿意保留我现在拥有的设置(在管理界面中)。

【问题讨论】:

  • 您必须同步访问bool _disable。你失去了并行执行,这在这个例子中很好,因为Console.WriteLine 无论如何都是同步的。
  • @Ginosaji:这是一个简单的例子,但在实际例子中,同步会对性能产生严重影响。
  • 您仍然可以并行处理消息并同步日志记录。正如我所说,Console.WriteLine 无论如何都是同步的,所以锁定 bool _disable 不会有任何损失。
  • @Ginosaji: Console.WriteLine 没有在真正的解决方案中使用。假设我们不能连续记录 - 它需要能够一次处理许多日志。

标签: c# multithreading circular-dependency


【解决方案1】:

您需要做的就是禁用当前堆栈帧。现在您可以使用反射检查堆栈帧并查看它是否已被调用,但有一个更简单的方法。每个线程都有一个堆栈帧。所以制作静态变量[ThreadStatic]

   [ThreadStatic] 静态 bool _disable = false;

这是如何工作的?

http://msdn.microsoft.com/en-us/library/system.threadstaticattribute.aspx

“表示静态字段的值对于每个线程都是唯一的。”

编辑:然而,仅此可能还不够。您可能想要的是每个任务一个静态变量。现在,由于任务将按每个线程顺序执行,在这种特殊情况下,我认为这不是问题,除非记录器可能会在不禁用的情况下失败......我不确定在这种情况下会发生什么,但它可能需要您至少可以将内容包装在 try/finally 块中:

static void Main() //Main(string[] args)
{
    string[] messages = "this is a test.  but it's going to be an issue!".Split(' ');

    Parallel.ForEach(messages, Log);

    Console.ReadLine();
}
[ThreadStatic]
static bool _disable = false;
public static void Log(string message)
{
    if (_disable)
    {
        return;
    }
    try {
        _disable = true;
        Console.WriteLine(GetPrefix() + message);
    } finally {
        _disable = false;
    }
}

public static string GetPrefix()
{
    Log("Getting prefix!");
    return "Prefix: ";
}

编辑二:从http://msdn.microsoft.com/en-us/library/dd460712.aspx 看来,一旦一组任务中的任何一个在任务委托之外引发异常,就不能保证执行任何剩余的任务。最好在您的委托中处理这些特殊情况。

【讨论】:

【解决方案2】:

如何将日志方法拆分成:

public static void LogWithPrefix(string message)
{
    var prefix = GetPrefix();
    Log(prefix + message);
}

public static void Log(string message)
{
    Console.WriteLine(message);
}

【讨论】:

  • 前缀是必需的。在这种情况下,获取前缀表示获取日志记录设置。没有这些,我不知道记录什么或在哪里记录。
  • @zimdanen:那么,在您的原始示例中,Log("Getting prefix!"); 应该登录到哪里?
  • @JayC:我想禁用它,这样它就不会在任何地方记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-06
  • 2020-03-14
相关资源
最近更新 更多