gpt4 book ai didi

c# - 从 BackgroundWorker 转向日志类的 TPL

转载 作者:太空狗 更新时间:2023-10-30 01:17:19 24 4
gpt4 key购买 nike

我目前已经在旧 View 中编写了一个简单的事件记录器Backgroundworker类(class)。我正在尝试将其转换为 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是为了。我也只保留一个 List<LogEvent>EventLog类,以便用户可以在当前日志中搜索特定事件(未完全实现/完善)。

这是我当前的 TPL 解决方案。我已尽我所能将日志记录功能包装到 Task 中s 但我仍然觉得我应该有一个类似于 publish 的函数而不必直接放置 LogEvent进入 BlockingCollection<>这样我就可以在主 UI 的单独线程上运行日志记录。

还有更简洁的方法来停止 Task无需发送“特殊”LogEvent给他们break来自他们的循环?

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();
}
});
}
}
  1. 如何正确使用 CancellationToken取消这两个任务?我不明白如果 BlockingCollection正在阻塞,我总是必须“脉冲”集合以使其解除阻塞。
  2. 是否有一种“更干净”的方式来插入 LogEvent进入日志,而不必直接将其插入 LogEventQueue

最佳答案

现在您的代码不是 thread-safe因为你有这个:

public List<LogEvent> Events { get; private set; }

List<T>不是线程安全的,可以从外部代码更改。而且我根本看不出它是否被使用过。

另外,你真的应该使用 CancellationToken在你的代码中因为在其他情况下你会遇到麻烦:例如,你有 5 条消息在队列中,你决定取消你的工作。在这种情况下,检查 Shutdown Log只会在一段时间后中断循环,这会让您的类(class)的最终用户感到困惑。

此外, BlockingCollection<T>.Take 也有过载使用 CancellationToken 的方法, 但在取消的情况下你会得到 OperationCanceledException :

try
{
LogEvent anEvent = WriteQueue.Take(CancellationPending);
}
catch (OperationCanceledException ex)
{
// handle stop here;
}

死循环在多线程中是非常糟糕的做法,我建议不要使用它。

关于c# - 从 BackgroundWorker 转向日志类的 TPL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32230071/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com