我有一个LogManager,它使用一些异步方法来添加和读取(异步)observablecollection(两者都遇到相同的错误)。但是在运行时,我有时会在此集合中添加一个空值,但我无法找到它的来源。
我尝试使用简单的if在方法Log和async方法上添加断点,但是我没有得到任何空值。我遇到它们的唯一地方是当我试图将日志保存到文件中时,它们已经添加到集合中了。
起初我以为是因为我调用函数的速度太快(虽然可以取消,但是可以取消),因为当我从它们中检索值时,项目被添加到集合中。
现在,当我遇到一个空对象时,我通过从集合中删除空对象得到了部分'固定',但我发现它令人不安,因为它可能是一个我不应该丢失的日志。
我会继续搜索,如果有人注意到某些内容或希望我添加其他方法,我曾经更好地了解请告诉我,任何帮助表示赞赏!
这是我的异步SaveLogs函数和SaveLoop:
private static Task SaveLoop(CancellationTokenSource cancel)
{
return Task.Run(async () =>
{
while (true)
{
int count = MessageList.Count;
if (count > SavedLogCount)
{
await SaveLogs();
}
if (cancel.IsCancellationRequested)
break;
}
}, cancel.Token);
}
private static Task SaveLogs(bool startLogging = false)
{
return Task.Run(() =>
{
int count = MessageList.Count;
int savedLogCount = SavedLogCount;
int logsSaved = 0;
Console.WriteLine("{0} logs to save", (count - savedLogCount));
SavedLogCount = count;
if (count > savedLogCount)
{
string logPath = "logs\\";
string logFile = StartTime.ToString("MM-dd-yy_HH-mm-ss") + ".log";
string fullPath = Path.Combine(logPath, logFile);
if (!Directory.Exists(logPath))
{
Directory.CreateDirectory(logPath);
}
using (StreamWriter writer = new StreamWriter(fullPath, !startLogging))
{
int item = -1;
try
{
for (int i = savedLogCount; i < count; i++)
{
item = i;
LogMessage currentMessage = MessageList[i];
writer.Write("({0})", i);
if (currentMessage == null)
{
Console.WriteLine("Current message is null, setting count back to {0} and removing", i);
writer.WriteLine("Null message");
count = i;
// This is something I don't really want to use
MessageList.Remove(null);
break;
}
else
{
writer.Write("[{0}][{1}] ", currentMessage.Time.ToString("HH:mm:ss:fff"), currentMessage.Level.ToString());
writer.Write(currentMessage.Message);
writer.WriteLine(" ({0} - {1})", currentMessage.Method, currentMessage.Location);
}
logsSaved++;
}
}
catch (Exception e)
{
Console.WriteLine("Error on item {0}", (savedLogCount + item));
Console.WriteLine("Message: {0}", e.Message);
Console.WriteLine(e.StackTrace);
}
}
SavedLogCount = count;
}
});
}
如果没有,请提前告诉我,我做错了什么或者我可以改进的地方!
改进的日志方法:
public static void Log(LogMessage logMessage, [CallerMemberName]string method = "", [CallerFilePath]string path = "")
{
logMessage.SetCaller(method, path);
if (!logQueue.Post(logMessage))
Console.WriteLine("Could not add message to queue");
}
新的AddLogsToCollection方法:
private static async Task AddLogsToCollection(BufferBlock<LogMessage> queue)
{
// Repeat while logQueue is not empty
while (await queue.OutputAvailableAsync())
{
LogMessage logMessage = await queue.ReceiveAsync();
// Add to MessageList
MessageList.Add(logMessage);
// Add to CurrentMessageList
CurrentMessageList.Add(logMessage);
// Add to FilteredMessageList
if (logMessage.Level >= FilterLevel)
{
FilteredMessageList.Add(logMessage);
}
}
}
改进了SaveLoop任务:
private static Task SaveLoop(CancellationTokenSource cancel)
{
return Task.Run(async () =>
{
while (true)
{
int logsInQueue = logQueue.Count;
// Check if there are any new logs to be processed
if (logsInQueue != 0)
{
// Copy queue to temporary currentQueue, so new logs can be added at the mean time
BufferBlock<LogMessage> currentQueue = logQueue;
logQueue = new BufferBlock<LogMessage>();
// Mark the currentQueue as complete
currentQueue.Complete();
// Start adding the logs to the collections
Task addLogsToCollection = AddLogsToCollection(currentQueue);
// Wait for the queue to be empty
await Task.WhenAll(addLogsToCollection, currentQueue.Completion);
// Save current logs
SaveLogs();
}
if (cancel.IsCancellationRequested)
break;
}
}, cancel.Token);
}
更改了SaveLogs方法:
private static void SaveLogs()
{
string logPath = "logs\\";
string logFile = StartTime.ToString("yyyy-MM-dd_HH-mm-ss") + ".log";
string fullPath = Path.Combine(logPath, logFile);
bool append = File.Exists(fullPath);
if (!Directory.Exists(logPath))
Directory.CreateDirectory(logPath);
int messageCount = MessageList.Count;
using (StreamWriter writer = new StreamWriter(fullPath, append))
{
for (int i = SavedLogCount; i < messageCount; i++)
{
LogMessage currentMessage = MessageList[i];
writer.Write("[{0}][{1}] ", currentMessage.Time.ToString("HH:mm:ss:fff"), currentMessage.Level.ToString());
writer.Write(currentMessage.Message);
writer.WriteLine(" ({0} - {1})", currentMessage.Method, currentMessage.Location);
}
}
SavedLogCount = messageCount;
}
AddLogToCollection:
private static Task AddLogToCollection(LogMessage logMessage)
{
return Task.Run(() =>
{
// Add to MessageList
MessageList.Add(logMessage);
// Add to CurrentMessageList
CurrentMessageList.Add(logMessage);
// Add to FilteredMessageList
if (logMessage.Level >= FilterLevel)
{
FilteredMessageList.Add(logMessage);
}
});
}
SaveLoop方法:
private static async Task SaveLoop(CancellationTokenSource cancel)
{
string logPath = "logs\\";
string logFile = StartTime.ToString("yyyy-MM-dd_HH-mm-ss") + ".log";
string fullPath = Path.Combine(logPath, logFile);
if (!Directory.Exists(logPath))
Directory.CreateDirectory(logPath);
using (FileStream fileStream = new FileStream(fullPath, FileMode.Append, FileAccess.Write, FileShare.Read, 8192, useAsync: true))
{
using (StreamWriter writer = new StreamWriter(fileStream))
{
while (true)
{
Console.WriteLine("writting logs");
LogMessage logMessage = await LogQueue.ReceiveAsync(cancel.Token);
await writer.WriteAsync(String.Format("({0}) ", logMessage.LogID));
await writer.WriteAsync(String.Format("[{0}][{1}] ", logMessage.Time.ToString("HH:mm:ss:fff"), logMessage.Level.ToString()));
await writer.WriteAsync(logMessage.Message);
await writer.WriteLineAsync(String.Format(" ({0} - {1})", logMessage.Method, logMessage.Location));
await AddLogToCollection(logMessage);
}
}
}
}
启动和停止循环方法:
private static void StartSaveLoop()
{
SaveLoopToken = new CancellationTokenSource();
SaveLoopTask = SaveLoop(SaveLoopToken);
Console.WriteLine("Loop started!");
}
private static void StopSaveLoop()
{
Console.WriteLine("Stop requested");
SaveLoopToken.Cancel();
while (!SaveLoopTask.IsCompleted)
{
Console.WriteLine(SaveLoopTask.Status.ToString());
Thread.Sleep(100);
}
Console.WriteLine("Loop stopped!");
}
答案 0 :(得分:3)
我建议您使用BufferBlock<LogMessage>
;它是一个async
- 准备好的生产者/消费者队列。
如果您的平台上没有BufferBlock
,则可以使用an async
-ready producer/consumer queue I built around a Queue
。它使用async
locks(condition variables)中的AsyncEx library和contains the async
-ready producer/consumer queue。
更新:您的帖子看起来不错,但您的收到不必要的复杂。我会做这样的事情:
private static async Task SaveLoop(CancellationTokenSource cancel)
{
string logPath = "logs\\";
string logFile = StartTime.ToString("yyyy-MM-dd_HH-mm-ss") + ".log";
string fullPath = Path.Combine(logPath, logFile);
if (!Directory.Exists(logPath))
Directory.CreateDirectory(logPath);
using (FileStream fileStream = new FileStream(fullPath, FileMode.Append, FileAccess.Write, FileShare.Read, 8192, useAsync: true)
using (StreamWriter writer = new StreamWriter(fileStream))
{
while (true)
{
LogMessage logMessage = await queue.ReceiveAsync(cancel);
await writer.WriteAsync(string.Format("[{0}][{1}] ", currentMessage.Time.ToString("HH:mm:ss:fff"), currentMessage.Level.ToString()));
await writer.WriteAsync(currentMessage.Message);
await writer.WriteLineAsync(string.Format(" ({0} - {1})", currentMessage.Method, currentMessage.Location));
}
}
}