C#启动新任务时取消之前的IO保存任务

问题描述 投票:0回答:0

我正在尝试为我的成就系统创建一个异步保存数据的任务。该任务每 1 分钟从主代码运行一次,但也可以在任何其他时间运行。


我想实现这些目标:

  1. 一次只能运行一个保存任务。
  2. 一旦保存请求到达,之前的保存任务(如果存在)应该被取消。
  3. 新任务应该在前一个任务完成取消并且前一个任务的资源被清除后开始。
  4. 如果队列中有保存任务还没有开始,则不应创建新任务。
  5. 请求启动任务时,应该可以在主线程上等待任务完成。如果因为之前的任务还没有开始,所以还没有创建新的任务,那么就需要等待还没有开始的任务完成。
  6. 当前任务执行完毕后,它占用的所有资源都应该被释放。

到目前为止,我尝试了不同的选择,这是最后一个:

数据仓库和保存任务调度器:

public class AchievementSaveTask : ISafeMemory {
    private bool cancelled;
    private Task currentSource;

    private ConcurrentDictionary<int, AchievementData> aDatas;

    public AchievementSaveTask() => aDatas = MouseAchievementFile.Read();

    public async Task StartWriting() {
        await CancelWrite();

        currentSource = Task.Run(() => MouseAchievementFile.Write(aDatas, cancelled));
        await currentSource;
        currentSource.Dispose();
        currentSource = null;
    }

    public async Task CancelWrite() {
        if (currentSource != null) {
            cancelled = true;
            await currentSource;
            cancelled = false;
            currentSource.Dispose();
            currentSource = null;
        }
    }
}

将数据写入文件的类:

public class MouseAchievementFile {
    private const string FOLDER = "MouseWorld/Achievement", FILE = "achievements.mdat";

    public static void Write(ConcurrentDictionary<int, AchievementData> data, in bool cancelled = false) {
        var pathDir = Combine(Main.SavePath, FOLDER);
        if (!Directory.Exists(pathDir)) Directory.CreateDirectory(pathDir);
        //Execution stops somewhere here, why?
        using (var bw = new BinaryWriter(File.Open(Combine(pathDir, FILE), FileMode.Create))) {
            foreach ((_, var value) in data) {
                var datas = value.GetRawData();
                foreach (var currData in datas) {
                    if (!CheckWrite(bw, currData, cancelled)) {
                        datas.Clear();
                        return;
                    }
                }
                datas.Clear();
            }
        }
    }

    private static bool CheckWrite(BinaryWriter bw, string source, in bool cancelled) {
        if (!cancelled) {
            bw.Write(source);
            return true;
        }
        else {
            bw.Flush();
            bw.Close();
            bw.Dispose();
            return false;
        }
    }
}

扩展方法:

public static void HandleException(this Task source, Action<Exception> exceptionHandler) {
    source.ContinueWith(t => exceptionHandler(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
}

电话:

if (wait) achievementSaveTask.StartWriting().Wait();
            else achievementSaveTask.StartWriting().HandleException(t => MWMod.Instance.Logger.WarnFormat("MAChievementManager.SaveAchievements() - WARNING! Could not save dict. Exception: {0}\r{1}", t.Message, t.StackTrace));

更新:

经过一些实验,我尝试更改我的代码以使用异步等待。虽然它在控制台应用程序中工作(向控制台输出一个字符串),但在实际情况下,我需要编写一个文件,程序会冻结。根据经验,我发现这大约发生在创建 BinaryWriter 时。我猜这是一个僵局。

评论里说了,BinaryWriter不适合异步写,但是写二进制形式的数据对我来说很重要,我不明白我怎么拒绝。

对此,出现以下问题:

  1. 为什么会出现冻结?
  2. 我可以使用 StreamWriter 来避免这个问题吗?
  3. 可以用StreamWriter写二进制形式的数据吗? (目标是使用文本编辑器使输出文件不可读和不可编辑)
c# .net asynchronous io task
© www.soinside.com 2019 - 2024. All rights reserved.