循环依赖与跨领域的关注

时间:2013-07-18 17:11:18

标签: c# multithreading circular-dependency

因此,这个例子试图给出一个我想要修改的更大系统的简单视图(即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方法。

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

2 个答案:

答案 0 :(得分:1)

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

[ThreadStatic] static bool _disable = false;

这是如何运作的?

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

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

编辑:然而,仅此一点可能还不够。您可能想要的是每个TASK的一个静态变量。现在,由于每个线程将按顺序执行任务,在这种特殊情况下我不认为这是一个问题,除非记录器可能在没有禁用的情况下失败...而且我不确定在这种情况下会发生什么,但可能需要你至少在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: ";
}

编辑II:从http://msdn.microsoft.com/en-us/library/dd460712.aspx开始,似乎任何一组任务都会在任务委托之外抛出异常,您无法保证执行任何剩余的任务。最好在你的代表处理这些特殊情况。

答案 1 :(得分:0)

如何将日志方法拆分为:

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

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