问题
So, this example is contrived to try to give a simple view of a much larger system I am trying to modify (namely, Orchard CMS). As such, it may not be perfect.
I am trying to create a logging system that is managed through settings. The problem I'm running into is that retrieving the settings causes logging to occur. Here's a simple example that hopefully describes the problem:
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: ";
}
This is an obvious StackOverflowException
. However, how can I resolve it? I can't just disable the logging until I get the response from GetPrefix
, because I may miss logs. (In fact, in this simple example, I miss all but the first.)
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: ";
}
(^Bad.)
Note that I do not currently have control over the GetPrefix
method, only the Log
method.
I'm not sure if there's a way to resolve this; I may need to put the settings elsewhere (such as the config or a separate settings file). However, if anyone has ideas or suggestions, I'd be happy to try anything, as I'd prefer to leave the settings as I have them now (which is in an admin interface).
回答1:
All you need to do is to disable on the current stack frame. Now you could use reflection to go over the stack frame and see if it's been called but there's a much simpler method. You have a stack frame for each thread. So make the static variable [ThreadStatic]
[ThreadStatic] static bool _disable = false;
How does this work?
http://msdn.microsoft.com/en-us/library/system.threadstaticattribute.aspx
"Indicates that the value of a static field is unique for each thread."
Edit: that alone might not be enough, however. What you probably want is one static variable per TASK. Now, as tasks would be executed sequentially per thread, in this particular case I don't think it's an issue, unless loggers can potentially fail without disabling... and I'm not sure what happens in that case, but it might require you to at the very least wrap things in a try/finally block:
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: ";
}
Edit II: From http://msdn.microsoft.com/en-us/library/dd460712.aspx it seems that once any of a set of tasks throws an exception outside of the task delegate, you are not guaranteed execution of any remaining tasks. It's best to handle those exceptional cases in your delegate.
回答2:
How about splitting the log method into:
public static void LogWithPrefix(string message)
{
var prefix = GetPrefix();
Log(prefix + message);
}
public static void Log(string message)
{
Console.WriteLine(message);
}
来源:https://stackoverflow.com/questions/17729907/circular-dependency-with-cross-cutting-concern