C# 创建使用线程的静态类的最佳方法是什么?
假设我正在设计一个简单的日志类(是的,我知道已经有了这样的类!),我希望该类是静态的,这样我的其余代码就可以调用它,而不必先实例化它。也许是这样的:C# 创建使用线程的静态类的最佳方法是什么?,c#,multithreading,asp.net-3.5,C#,Multithreading,Asp.net 3.5,假设我正在设计一个简单的日志类(是的,我知道已经有了这样的类!),我希望该类是静态的,这样我的其余代码就可以调用它,而不必先实例化它。也许是这样的: internal static class Log { private static string _logFile = ""; internal static void InitializeLogFile(string path) { ... } internal static void
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)
{
...
}
}
现在,我想让内部人员启动自己的线程,并以异步方式执行,可能使用BackgroundWorker来帮助简化事情。我应该在每个方法中创建一个新的BackgroundWorker,创建一个静态BackgroundWorker作为静态类的私有属性,还是有什么东西我完全忽略了?我认为我的建议并不完全符合您的期望,但我希望它仍然有用:
- 不要使用静态类。相反 使用常规课程,并举行一次 它的实例;使用依赖项 喷射引擎有很大的帮助 这个(我用的)和它 工作正常)。如果您也为日志类定义了一个接口,那么您的代码将更易于测试
- 至于穿线的东西,如果我 正确理解你想要的 要在中执行的日志记录工作 分开的线程。你确定吗 你真的需要这个吗?记录器应该 要足够轻,这样你就可以 简单地调用“Write”方法和 希望您的应用程序 性能不会受到影响
就我的2美分…不久前我自己创建了一个线程安全日志类。我是这样用的
Logging obj = new Logging(filename);
Action<string> log = obj.RequestLog();
Logging obj=新的日志记录(文件名);
操作日志=obj.RequestLog();
RequestLog将返回一个写入自己队列的匿名方法。因为Q对于1读写器是线程安全的,所以在调用log()时不需要使用任何锁
实际的日志对象将创建一个在后台运行的新线程,并定期检查所有队列。如果Q中有一个字符串,它会将其写入缓冲文件流
我在读取线程中添加了一点额外的代码,这样每次在队列中传递时,如果没有写入任何内容,它将额外休眠10毫秒,最多可达100毫秒。这样线就不会绕得太多。但如果有大量的写作,它会每10毫秒对Qs进行一次投票
下面是请求队列的返回代码的snpit。“this.blNewData=true”是这样的,所以我不需要访问每个Q来查看是否写入了任何新数据。不涉及锁,因为假阳性仍然不起作用,因为所有Qs无论如何都是空的
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;
};
}
公共操作NewQueueLog()
{
队列TMPQUE=新队列(32);
锁(输出队列)
{
添加(tmpQueue);
}
返回(字符串输出)=>
{
TMPQUE.Enqueue(输出);
this.blNewData=true;
};
}
最后,写入日志是无锁的,这在大量线程写入时非常有用。您只希望每个日志文件/db有一个线程。否则,日志中项目的顺序不可靠。拥有一个后台线程,该线程从线程安全队列中提取并执行写入操作。您肯定不希望在每次调用方法时启动一个新线程或
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);
}
}
}
内部静态类日志
{
私有静态BlockingCollection s_队列=新建BlockingCollection();
静态日志()
{
var线程=新线程(运行);
thread.IsBackground=true;
thread.Start();
}
私有静态无效运行()
{
while(true)
{
string line=s_Queue.Take();
//在此处添加代码以将该行附加到日志。
}
}
内部静态void WriteLine(参数字符串[]项)
{
foreach(项中的字符串项)
{
s_队列。添加(项);
}
}
}
打得好
您肯定希望日志记录操作与执行日志记录的代码在单独的线程中发生。例如,当记录器将事件记录到文件中时,访问器方法(如“logEvent(myEvent)”)不应阻止文件I/O操作
创建一个队列,以便访问者只需将项目推送到队列上。这样,您的代码在尝试记录事件时不应阻塞
启动第二个线程以清空内部事件队列。此线程可以在logger类的静态私有方法上运行
当您试图确保底层事件队列的线程安全时,就会出现性能缺陷。每次弹出或推送到队列之前,您都需要获得队列上的锁
希望这能有所帮助。在应用程序中使用非静态类和单个静态(单例)实例会更好。静态类对于扩展方法是必需的,但最好避免使用。@bzlm BackgroundWorker适用于.NET;不只是WinForms@Aaron@bzlm:虽然BackgroundWorker可以在.NET应用程序中的任何位置工作,但它并不理想,也不是有意的。在这种情况下,线程池是首选。@SnOrfus虽然BackgroundWorker在.NET 2中出现,WinForms是焦点,但没有理由不继续