免责声明:我知道诸如Thread Safe C# Singleton Pattern之类的问题,但是这些问题无法回答我的问题。
让我先描述我的C#项目来陈述我的问题:我有一个类JobProcessor
,它接受类JobTicket
的对象,该对象保存有关“做什么”的信息。 JobProcessor
使用外观模式根据JobTicket
中的参数来协调其他类别,例如有关该作业的.log文件的路径。为此,我有一个Singleton类Logger
,为此JobProcessor
在作业的开始处设置路径,然后其他所有类都将仅调用Logger.Log(message)
。在我到达要使用类JobManager
进行并行化的程度之前,该类可以正常工作,该类接受类JobTicket
的对象并将它们保存在队列中。 JobManager
使用队列中的JobProcessor
实例化新的JobTicket
。最多可并行4个。
现在您已经可以想象发生了什么:如果一次运行多个JobProcessor
,则一个将覆盖Logger
的路径。如何确定Logger包含在JobProcessor中,而无需更改大量代码?我曾考虑过在Logger
类中有一个JobProcessor
字段,但随后我必须将其传递给其他每个要使用它的类,因为通过Facade模式,Facade知道它使用的每个类,但是每个类它使用的它不知道外观。
事后看来,记录器的单例模式没有想象的那么聪明,但是将其传递给每个班级似乎很乏味。有没有更聪明的方法?每个线程可以有一个单例吗?
public static class JobManager
{
private static BlockingCollection<JobTicket> _jobs = new BlockingCollection<JobTicket>();
public static void AddJob(JobTicket job)
{
_jobs.Add(job);
}
public static void StartConsumer()
{
Task.Factory.StartNew(() =>
{
void processJob()
{
while (!_jobs.IsCompleted)
{
var job = _jobs.Take();
try
{
using (JobProcessor processor = new JobProcessor(job))
{
processor.Start();
}
}
catch
{
// Alert something that an error happened
}
}
};
// process 4 jobs in parallel
Parallel.Invoke(processJob, processJob, processJob, processJob);
});
}
}
public class JobProcessor
{
private JobTicket jobticket;
public JobProcessor(JobTicket jobticket) {
this.jobticket = jobticket;
// ...
}
public void Start() {
if(!(jobticket.LogPath is null)) {
var logger = new TextLogger(jobticket.LogPath);
Logger.SetLogger(logger);
}
Logger.Log("Job started");
// Process job
var a = new ClassA(...);
if (jobticket.x)
a.DoSomething();
else
a.DoSomethingElse();
}
}
public class ClassA {
//...
public void DoSomething() {
//...
Logger.Log("I did something");
}
public void DoSomethingElse() {
//...
Logger.Log("I did something else");
}
}
// I know this is not thread-safe, but what I want is one Logger instance per JobProcessor and not one Logger instance for all JobProcessors.
public static class Logger
{
private static BaseLogger _logger = new ConsoleLogger();
public static void Log(string message) {
_logger.Log(message);
}
public static void SetLogger(BaseLogger logger)
{
_logger = logger;
}
}
public abstract class BaseLogger
{
public abstract void Log(string message);
}
public class TextLogger : BaseLogger
{
public readonly string path;
public TextLogger(string path) : base()
{
this.path = path;
}
public override void Log(string message)
{
File.AppendAllText(path, message);
}
}
public class ConsoleLogger : BaseLogger
{
public override void Log(string message)
{
Console.WriteLine(message);
}
}
您可以在静态记录器类中创建Dictionary<ThreadId, BaseLogger>
类。您将为每个线程拥有自己的记录器。
另外,您可以将SetLogger
签名更改为类似void SetLogger(Func<BaseLogger> loggerFactory)
的样式,以创建需要在Logger
内部使用的记录器数量。