提问人:Unable error 提问时间:4/29/2023 最后编辑:Unable error 更新时间:4/30/2023 访问量:76
C# 启动新任务时取消上一个 IO 保存任务
C# Cancel the previous IO save Task when starting a new one
问:
我正在尝试为我的成就系统异步保存数据。此任务每 1 分钟从主代码运行一次,但也可以在任何其他时间运行。
我想实现这些目标:
- 一次只能运行一个保存任务。
- 一旦保存请求到达,就应立即取消上一个保存任务(如果存在)。
- 新任务应在上一个任务完成取消且上一个任务的资源已清除后启动。
- 如果队列中存在尚未启动的保存任务,则不应创建新任务。
- 请求启动任务时,应该可以等待任务在主线程上完成。如果由于上一个任务尚未启动而尚未创建新任务,则需要等待尚未启动的任务完成。
- 当前任务执行完毕后,应释放其使用的所有资源。
到目前为止,我已经尝试了不同的选项,这是最后一个:
数据仓库和保存任务计划程序:
public class AchievementSaveTask {
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));
更新:
经过一些实验,我尝试将代码更改为使用 async-await。尽管它在控制台应用程序中工作(将字符串输出到控制台),但在实际情况下,我需要编写文件,程序会冻结。根据经验,我发现这大约发生在创建 BinaryWriter 时。我猜有一个僵局正在发生。
正如他们在评论中所说,BinaryWriter 不适合异步编写,但以二进制形式编写数据对我来说很重要,我不明白我怎么能拒绝它。
在这方面,出现了以下问题:
- 为什么会发生冻结?
- 我可以通过使用 StreamWriter 来避免这个问题吗?
- 我可以使用 StreamWriter 以二进制形式写入数据吗?(目标是使用文本编辑器使输出文件不可读和不可编辑)
答: 暂无答案
评论
BinaryWriter
不兼容异步,无法使用 有什么原因吗?CancellationToken
BinaryWriter
BinaryWriter
StreamWriter
StreamWriter
CancellationToken