gpt4 book ai didi

c# - .NET 任务/TPL 测试和模拟? (或不正确的用法?)

转载 作者:太空宇宙 更新时间:2023-11-03 11:25:32 25 4
gpt4 key购买 nike

我有一个类,它抽象了在远离我的 WPF MVVM View 模型的后台线程上执行长时间运行的方法。我也有此类接口(interface)和 IoC 注入(inject)到我的大多数 View 模型中。

public interface IAsyncActionManager : INotifyPropertyChanged
{
/// <summary>
/// Sets and gets the IsBusy property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
bool IsBusy { get; }

Task StartAsyncTask(Action backgroundAction);
}

我的 View 模型以各种方式使用此类,例如:

private void LoadStuff()
{
ActionManager.StartAsyncTask(() => { // Load stuff from database here });
}

在我的一些 XAML 中,我直接绑定(bind)到 IsBusy 属性:

<Grid Cursor="{Binding ActionManager.IsBusy, Converter={Converters:BusyMouseConverter}}">

无论如何 - 现在你已经了解了背景,我现在正在尝试做一些更花哨的事情:

private Task _saveChangesTask;
public void SaveChanges()
{
if (_saveChangesTask != null && _saveChangesTask.Status != TaskStatus.Running)
return;

_saveChangesTask = ActionManager.StartAsyncTask(() =>
{
// Save stuff here - slowly
});
}

这是简化的,因为我还通过 WPF 在其 View 中使用 CanExecute 等的 Command 对象将其连接起来,但是任务的这种“缓存”是为了使保存操作不会运行两次。

现在开始讨论这个问题,我想对这个逻辑进行单元测试 - 我该怎么做?我已经尝试在我的测试中使用 TaskCompletionSource,但我无法让我的任务进入“正在运行”状态......?

var tcs = new TaskCompletionSource<object>();
// tcs.Task.Status is now WaitingForActivation

// tcs.Task.Start(Synchronous.TaskScheduler); // Doesn't work - throws an Exception.

A.CallTo(() => mockAsyncActionManager.StartAsyncTask(A<Action>._, A<Action<Task>>._)).Returns(tcs.Task);

有人知道吗?我可以这样做吗?

我有一个想法,我错误地使用了 TPL - 我不应该依赖任务状态 - 但不确定如何以另一种方式实现类似的事情(欢迎提出建议)。

干杯,

最佳答案

我相信这里的问题确实在于(如您所说)检查 Status property .

TaskStatus enumeration表示 Task实例不仅具有运行/不运行的二元状态,而且可以处于多种状态。

创建任务时,取决于TaskScheduler ,它会将 Task 置于以下状态 before Running 状态:

  • 已创建 - 任务已初始化但尚未安排。
  • WaitingForActivation - 任务正在等待 .NET Framework 基础结构在内部激活和安排。
  • WaitingToRun - 任务已安排执行但尚未开始执行。

这样,您对 RunningTaskStatus 的检查可能会失败,因为它处于上述状态之一。

我建议您只需检查Task 的引用;如果它是null,则创建一个新的Task,否则,直接返回。

这里的假设是对 SaveChanges 的调用意味着在对象上调用一次(或者在保存完成之前不做任何事情)。

如果您要再次调用该方法(大概是因为已经进行了其他更改),您应该在 Task 上继续,这将设置对 Task< 的引用null 当操作完成时。这样,当第二次调用 SaveChanges 时,对引用的检查将会成功。

旁注,I've pointed out in the comments that you have a race condition .如果您打算在延续中将对 Task 的引用设置回 null,那么您需要以线程安全的方式执行检查和赋值(因为延续将在另一个线程上运行),像这样:

private Task _saveChangesTask;

// Used to synchronize access to _saveChangesTask
private readonly object _saveChangesTaskLock = new object();

public void SaveChanges()
{
// Guard access to the reference.
lock (_saveChangesTaskLock)
{
// Check and assign.
if (_saveChangesTask != null) return;

_saveChangesTask = ActionManager.StartAsyncTask(() =>
{
// Save stuff here - slowly

// Done saving stuff here - slowly
// (BTW, is the above a reference from "True Lies"?)
// Remove reference to task. This is on another thread
// so using a lock again is ok.
// Guard access to the reference.
lock (_saveChangesTaskLock) _saveChangesTask = null;
});
}
}

关于c# - .NET 任务/TPL 测试和模拟? (或不正确的用法?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9486076/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com