创建使用线程的静态类的最佳方法是什么?

时间:2011-02-08 17:29:11

标签: c# multithreading asp.net-3.5

假设我正在设计一个简单的日志记录类(是的 - 我知道那些已经存在的东西!)我希望该类是静态的,所以我的其余代码可以无需先实例化它就可以调用它。也许是这样的:

internal static class Log
{
    private static string _logFile = "";

    internal static void InitializeLogFile(string path)
    {
        ...
    }
    internal static void WriteHeader()
    {
        ...
    }
    internal static void WriteLine(params string[] items)
    {
        ...
    }
}

现在,我希望内部启动自己的线程并以Asynch方式执行,可能使用BackgroundWorker来简化操作。我应该在每个方法中创建一个新的BackgroundWorker,创建一个静态BackgroundWorker作为静态类的私有属性,还是有一些我完全忽略的东西?

5 个答案:

答案 0 :(得分:2)

您绝对不希望在每次调用方法时启动新线程或BackgroundWorker。我会在这里使用生产者 - 消费者模式。事实证明,这是一种常见的模式,微软为我们提供了BlockingCollection类,它极大地简化了实现。这种方法的好处在于:

  • 只需要一个额外的线程
  • Log方法将具有异步语义
  • 保留日志消息的时间顺序

以下是一些开始使用的代码。

internal static class Log
{
  private static BlockingCollection<string> s_Queue = new BlockingCollection<string>();

  static Log()
  {
    var thread = new Thread(Run);
    thread.IsBackground = true;
    thread.Start();
  }

  private static void Run()
  {
    while (true)
    {
      string line = s_Queue.Take();
      // Add code to append the line to the log here.
    }
  }

  internal static void WriteLine(params string[] items)
  {
    foreach (string item in items)
    {
      s_Queue.Add(item);
    }
  }
}

答案 1 :(得分:1)

您只希望每个日志文件/ db有1个线程。否则,日志中的项目顺序不可靠。有一个后台线程从线程安全队列中提取并执行写入。

答案 2 :(得分:0)

我认为我的建议并不完全符合您的期望,但我希望它仍然有用:

  • 不要使用静态类。代替, 使用普通班并持有一个 它的实例(singleton pattern);使用依赖 注射引擎有很多帮助 这个(我使用MS Unity和它 工作良好)。如果您还为日志记录类定义了一个接口,那么您的代码将更加可测试。
  • 至于线程的东西,如果我 了解你想要的关联 记录工作要执行 单独的线程。你确定吗? 你真的需要这个吗?记录器应该 足够轻,以便你可以 简单地调用“写”方法和 期待您的申请 表现不会受到影响。

最后一点:您提到了BackgroundWorker类,但如果我没有错,这个类适用于桌面应用程序,而不是ASP.NET。在这种环境中,您应该使用the ThreadPool class之类的东西。

只需2欧分......

答案 3 :(得分:0)

我自己创建了一个线程安全日志记录类。我用过这样的东西。

Logging obj = new Logging(filename);
Action<string> log = obj.RequestLog();

RequestLog将返回写入其自己的Queue的匿名方法。因为Q对于1个读取器/写入器是线程安全的,所以在调用log()

时我不需要使用任何锁

实际的Logging对象将创建一个在后台运行的新线程,并定期检查所有队列。如果Q中有一个字符串,它会将其写入缓冲的文件流。

我在读取线程中添加了一些额外的代码,因此对于它在队列中进行的每次传递,如果没有写入,它将额外休眠10毫秒,最多100毫秒。这样线程就不会过多。但如果有大量的写作,它会每隔10毫秒轮询Q.

这是所请求队列的返回码的一个小部件。 “this.blNewData = true”是这样的,所以我不需要查看每个Q来查看是否有任何新数据被写入。没有锁定,因为假阳性仍无效,因为无论如何所有的Q都是空的。

OutputQueue是我循环查询的队列列表,以查看是否写入了任何内容。在调用NewQueueLog()并导致列表调整大小时,循环遍历列表的代码处于锁定状态。

public Action<String> NewQueueLog()
{
    Queue<String> tmpQueue = new Queue<String>(32);
    lock (OutputQueue)
    {
        OutputQueue.Add(tmpQueue);
    }
    return (String Output) =>
    {
        tmpQueue.Enqueue(Output);
        this.blNewData = true;
    };
}

最后,写入日志是免费的,这有助于许多线程在写。

答案 4 :(得分:0)

好的电话,

您肯定希望日志记录操作作为执行日志记录的代码在单独的线程中进行。例如,当记录器将事件记录到文件时,访问器方法(例如“logEvent(myEvent)”)不应阻止文件I / O操作。

创建一个队列,以便访问者只需将项目推送到队列中。这样,您的代码在尝试记录事件时不应阻止。

启动第二个线程以清空事件的内部队列。该线程可以在记录器类的静态私有方法上运行。

当您尝试确保基础事件队列的线程安全时,会出现性能缺陷。每次弹出或推入队列之前,您都需要获取队列锁定。

希望这有帮助。