C#单例模式在实现并行而不是并发处理后无法按预期工作

问题描述 投票:2回答:1

免责声明:我知道诸如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);
    }
}
c# parallel-processing singleton facade
1个回答
0
投票

您可以在静态记录器类中创建Dictionary<ThreadId, BaseLogger>类。您将为每个线程拥有自己的记录器。

另外,您可以将SetLogger签名更改为类似void SetLogger(Func<BaseLogger> loggerFactory)的样式,以创建需要在Logger内部使用的记录器数量。

© www.soinside.com 2019 - 2024. All rights reserved.