【发布时间】:2015-08-26 14:57:59
【问题描述】:
我目前在旧的Backgroundworker 类的视图中编写了一个简单的事件记录器。我正在尝试将其转换为 TPL 实现。
我在 C# 中没有足够的线程使用来真正喜欢其中一个而不是另一个,但我知道 TPL 正变得越来越受欢迎,我想尽可能地坚持使用它。另一个原因是,使用当前代码,我找不到使EventLog 类线程安全的简单方法。我发现自己使用BeginInvoke 从非 UI 线程写入日志,这对我来说似乎很乱。
所以这里是原始代码。
public class EventLog
{
public String LogPath { get; set; }
public List<LogEvent> Events { get; private set; }
public static EventLog Instance { get { return lazyInstance.Value; } }
private static readonly Lazy<EventLog> lazyInstance = new Lazy<EventLog>(() => new EventLog());
private EventLog()
{
Events = new List<LogEvent>();
LogPath = Assembly.GetExecutingAssembly().CodeBase;
LogPath = Path.GetDirectoryName(LogPath);
LogPath = LogPath.Replace("file:\\", "");
LogPath = LogPath + "\\Log.txt";
}
public override void publish(LogEvent newEvent)
{
Events.Add(newEvent);
if (!LogEventWriter.Instance.IsBusy)
LogEventWriter.Instance.RunWorkerAsync(LogPath);
LogEventWriter.Instance.LogEvents.Add(newEvent);
}
}
internal class LogEventWriter : BackgroundWorker
{
public BlockingCollection<LogEvent> LogEvents { get; set; }
public static LogEventWriter Instance { get { return lazyInstance.Value; } }
private static readonly Lazy<LogEventWriter> lazyInstance = new Lazy<LogEventWriter>(() => new LogEventWriter());
private LogEventWriter()
{
WorkerSupportsCancellation = true;
LogEvents = new BlockingCollection<LogEvent>();
}
protected override void OnDoWork(DoWorkEventArgs e)
{
if (e.Argument != null && e.Argument is String)
{
String logPath = (String)e.Argument;
using (StreamWriter logFile = new StreamWriter(logPath, true))
{
while (!CancellationPending)
{
LogEvent anEvent = LogEvents.Take();
logFile.WriteLine(anEvent.Message);
logFile.Flush();
if (anEvent.Message.Contains("Application Terminated"))
break;
}
logFile.Close();
}
}
e.Cancel = true;
}
}
我目前对日志的思路是在系统发生故障时尽快将日志写入文件,以便日志包含尽可能多的信息。这就是Backgroundworker 的用途。我还只是在EventLog 类中保留了一个List<LogEvent>,以便用户可以在当前日志中搜索特定事件(未完全实现/完善)。
这是我目前的 TPL 解决方案。我已尽我所能将日志记录功能包装到Tasks 中,但我仍然觉得我应该有一个类似于publish 的功能,而不必直接将LogEvents 放入BlockingCollection<> 中,这样我可以在主 UI 的单独线程上运行日志记录。
还有一种更简洁的方法来阻止Tasks,而不必从他们的循环中向break 发送“特殊”LogEvent 给他们?
public class EventLog
{
public static EventLog Instance { get { return lazyInstance.Value; } }
private static readonly Lazy<EventLog> lazyInstance = new Lazy<EventLog>(() => new EventLog());
public String LogPath { get; set; }
public ConcurrentQueue<LogEvent> Events { get; set; }
private EventLog()
{
Events = new ConcurrentQueue<LogEvent>();
WriteQueue = new BlockingCollection<LogEvent>();
LogEventQueue = new BlockingCollection<LogEvent>();
LogPath = Assembly.GetExecutingAssembly().CodeBase;
LogPath = Path.GetDirectoryName(LogPath);
LogPath = LogPath.Replace("file:\\", "");
LogPath = LogPath + "\\LogASDF.txt";
StartManager();
StartWriter();
}
public BlockingCollection<LogEvent> LogEventQueue { get; set; }
private void StartManager()
{
var writeTask = Task.Factory.StartNew(() =>
{
while (true)
{
LogEvent anEvent = LogEventQueue.Take();
Events.Enqueue(anEvent);
WriteQueue.Add(anEvent);
if (anEvent.Message.Contains("Application Terminated"))
break;
}
});
}
private BlockingCollection<LogEvent> WriteQueue { get; set; }
private void StartWriter()
{
var writeTask = Task.Factory.StartNew(() =>
{
using (StreamWriter logFile = new StreamWriter(LogPath, true))
{
while(true)
{
LogEvent anEvent = WriteQueue.Take();
logFile.WriteLine(anEvent.Message);
logFile.Flush();
if (anEvent.Message.Contains("Application Terminated"))
break;
}
logFile.Close();
}
});
}
}
- 如何正确使用
CancellationToken取消这两个任务?我不明白如果BlockingCollection被阻塞,我总是必须“脉冲”集合以使其解除阻塞。 - 是否有一种“更简洁”的方式可以将
LogEvent插入日志而不必直接将其插入LogEventQueue?
【问题讨论】:
-
LogEvent和EventLog对我来说是不好的命名...... -
是的,我知道.. 我还没有完全完善这段代码。
标签: c# multithreading task-parallel-library backgroundworker