0

私の LogManager には、while ループを実行してログ情報をテキスト ファイルに書き込む (そしてそれをコレクションに追加する) 非同期タスクがあります。このタスクをキャンセルしようとすると、ループが停止したように見えます (いくつかの Console.WriteLines で試しました) が、タスクは完了しません。

このクラスの主なメソッドは次のとおりです。

public static DateTime StartTime;
private static CancellationTokenSource SaveLoopToken;
private static Task SaveLoopTask;

public static void Start()
{
    LogMessage startMessage = new LogMessage("Logging Started");
    startMessage.SetCaller("StartLogging", "LogManager");
    StartTime = startMessage.Time;
    LogQueue.Post(startMessage);
    StartSaveLoop();
}

public static void Stop()
{
    LogMessage stopMessage = new LogMessage("Logging Stopped");
    stopMessage.SetCaller("StopLogging", "LogManager");
    LogQueue.Post(stopMessage);
    StopSaveLoop();
}

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)
    {
        Thread.Sleep(100);
    }

    Console.WriteLine("Loop stopped!");
}

private static void AddLogToCollection(LogMessage logMessage)
{
    // Add to MessageList
    MessageList.Add(logMessage);

    // Add to CurrentMessageList
    CurrentMessageList.Add(logMessage);

    // Add to FilteredMessageList
    if (logMessage.Level >= FilterLevel)
    {
        FilteredMessageList.Add(logMessage);
    }
}

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 LogQueue.ReceiveAsync(cancel.Token);

                AddLogToCollection(logMessage);

                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));

                if (cancel.IsCancellationRequested)
                    break;
            }
        }
    }
}

以前の Start/StopSaveLoop メソッド:

private static async void StartSaveLoop()
{
    SaveLoopToken = new CancellationTokenSource();
    Console.WriteLine("Loop started!");

    await SaveLoop(SaveLoopToken);
    Console.WriteLine("Loop stoped!");
}

private static void StopSaveLoop()
{
    Console.WriteLine("Stop requested");
    SaveLoopToken.Cancel();
}
4

1 に答える 1

1

私のブログ最近の MSDN の記事で説明しているデッドロック状態が発生していると思われます。Application開始イベントと終了イベントが WPF UI コンテキスト内で実行されている場合 (そう思われます)、UIスレッドでSaveLoop終了しようとします (イベントが完了を待っている UI スレッドをブロックしているTaskため、終了できません)。)。ExitTask

問題を無視することもできます (すべてのファイルは適切に閉じられていますが、バッファリングされたデータが失われる可能性があります)。または、クリーン シャットダウンをサポートするようにソリューションを微調整することもできます。

調整オプションの 1 つは、ログ ライターをバックグラウンド スレッドで実行することです (以前のようTask.Runにラップを使用するか、 for everyを使用します)。このアプローチの問題点は、コレクションが UI 要素にデータ バインドされている場合、うまく機能しない可能性があることです。SaveLoopConfigureAwait(false)awaitSaveLoopAddLogToCollection

于 2013-06-12T19:59:47.953 に答える