【发布时间】:2018-07-30 14:29:18
【问题描述】:
我遇到了下面正在执行的任务异步运行但在序列化程序读取内存流时无法取消的问题。当用户发出取消请求(通过按下取消按钮)时,将进行取消(从令牌调用方法 cancel())但任务继续。
服务等级:
从 Main 类中的 LoadHelper() 调用的异步方法
public async void StartTask(Action callback, CancellationToken token)
{
await Task.Run(callback, token);
}
主类:
private void LoadHelper()
{
_services.GetInstance<IThreadService>().StartTask(
() => LoadHelperAsync(), _cancelService.Token);
}
异步运行的方法
private void LoadHelperAsync()
{
var serializer = new DataContractSerializer(typeof(UserDatabase));
string selectedDir;
ComparisonResult = null;
_services.GetInstance<IOpenFileService>().DisplayOpenFileDialog(
"Select a saved comparison",
"Comparison File(*.xml; *.cmp)|*.xml;*.cmp",
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
out selectedDir);
if (!string.IsNullOrEmpty(selectedDir))
{
_dispatchService.BeginInvoke(() => IsExecuting = true);
using (FileStream fileStream = new FileStream(selectedDir, FileMode.Open, FileAccess.Read))
using (DeflateStream compressedStream = new DeflateStream(fileStream, CompressionMode.Decompress))
using (BufferedStream regularStream = new BufferedStream(fileStream))
{
Stream memoryStream;
//Use filename to determine compression
if (selectedDir.EndsWith(".cmp", true, null))
{
memoryStream = compressedStream;
}
else
{
memoryStream = regularStream;
}
Report("Loading comparison");
Report(0);
IsExecuting = true;
ComparisonResult = (UserDatabase)serializer.ReadObject(memoryStream);
memoryStream.Close();
fileStream.Close();
IsExecuting = false;
Report("Comparison loaded");
}
_dispatchService.BeginInvoke(() => IsExecuting = false);
_dispatchService.BeginInvoke(() => ViewResults.ExecuteIfAble());
}
else
{
Report("No comparison loaded");
}
取消代码:
此命令绑定到视图中的“取消”按钮。
CancelCompare = new Command(o => _cancelService.Cancel(), o => IsExecuting);
来自 CancellationService 类
public class CancellationService : ICancellationService, IDisposable
{
private CancellationTokenSource _tokenSource;
public CancellationService()
{
Reset();
}
public CancellationToken Token { get; private set; }
public void Cancel()
{
_tokenSource.Cancel();
}
public void Reset()
{
_tokenSource = new CancellationTokenSource();
Token = _tokenSource.Token;
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_tokenSource.Cancel();
_tokenSource.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
【问题讨论】:
-
为什么会有 async void?
LoadHelperAsync不是异步的,为什么要使用 -Async 后缀约定命名? -
请添加取消任务的方式
-
@RickyThomas 取消是合作的。调用
.Cancel()不会中止线程,它会发出令牌信号。 您的 操作必须接收令牌并检查它是否被提出。如果你想取消反序列化,你应该在LoadHelperAsync中使用异步方法并将令牌也传递给它们。 不是 BeginInvoke,本质上是Thread.Start或直接调用另一个线程,具体取决于_dispatchService是什么 -
@mason 代码从服务类异步运行。我认为原作者是因为这个才这样做的,但不幸的是我不能说他在后缀约定中的选择。
-
@RickyThomas 这段代码太复杂了。 cts 和令牌不是服务,它们是需要传递的原语。请求输入应该在异步操作本身之外执行。这就是
await的重点,它允许您在await调用之前和之后处理UI 线程。进度报告应使用IProgress<T>接口。查看 Stephen Toub 的Enabling Progress and Cancellation in Async APIs
标签: c# asynchronous cancellation cancellationtokensource