即使CancelIsRequested,Task.IsCompleted仍为false [英] Task.IsCompleted stays false even when CancelIsRequested

查看:134
本文介绍了即使CancelIsRequested,Task.IsCompleted仍为false的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在LogManager中,我有一个异步任务,该任务运行while循环以将日志信息写入文本文件(并将其添加到集合中).当我尝试取消此任务时,循环似乎停止了(我尝试了几次Console.WriteLines),但是该任务并未完成.

In my LogManager, I have an async task that runs a while loop to write log information to a text file (and add it to a collection). When I try to cancel this task, the loop seems to stop (I tried with several Console.WriteLines), but the task however doesn't complete.

这是此类的主要方法:

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方法:


Previous Start/StopSaveLoop methods:

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

推荐答案

我怀疑您可能会看到我描述 在最近的MSDN文章中.如果Application开始和退出事件是在WPF UI上下文中运行的(我怀疑是这样),则SaveLoop会尝试在UI线程上完成其Task(它不能完成,因为事件阻止了UI线程等待Task完成.

I suspect you may be seeing a deadlock situation that I describe on my blog and in a recent MSDN article. If the Application start and exit events are run within the WPF UI context (which I suspect they are), then the SaveLoop would try to finish its Task on the UI thread (which it cannot, because the Exit event is blocking the UI thread waiting for the Task to complete).

您可以忽略该问题(所有文件均已正确关闭,但缓冲的数据可能会丢失).或者,您可以调整解决方案以支持干净关闭.

You could just ignore the problem (all files are properly closed, but buffered data may be lost). Or you could tweak your solution to support a clean shutdown.

一项调整选项是在后台线程上运行日志编写器(如以前一样使用Task.Run来包装SaveLoop,或者对于SaveLoop中的每个await使用ConfigureAwait(false)).这种方法的问题在于,如果集合是数据绑定到UI元素的,则AddLogToCollection可能无法很好地发挥作用.

One tweaking option is to run the log writer on a background thread (either using Task.Run to wrap SaveLoop like you were doing before, or using ConfigureAwait(false) for every await in SaveLoop). The problem with this approach is that it may not play well with AddLogToCollection if the collections are data-bound to UI elements.

这篇关于即使CancelIsRequested,Task.IsCompleted仍为false的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆