いくつかの非同期メソッドを使用して (async)observablecollection に追加および読み取りを行う LogManager があります (どちらも同じエラーが発生します)。ただし、実行中にこのコレクションに null 値が追加されることがあり、それがどこから来ているのかわかりません。
メソッド Log と async メソッドに単純な if でブレークポイントを追加しようとしましたが、null 値が得られません。それらに遭遇する唯一の場所は、ログをファイルに保存しようとしているときです。ログは既にコレクションに追加されています..
最初は、値を取得している間にアイテムがコレクションに追加されたため、関数の呼び出しが速すぎたためだと思いました (これは while true ループにありますが、キャンセルできます)。
今のところ、ヌルオブジェクトに遭遇したときにコレクションからヌルオブジェクトを削除することで部分的に「修正」しましたが、欠落してはならないログである可能性があるため、気がかりです。
誰かが何かに気づいたり、私が以前よりよく理解していた他の方法を追加したい場合は、引き続き検索します。
非同期の 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;
}
最適化されたコード 2
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!");
}