我想建立一个可以立即响应事件的Rx订阅,然后忽略在指定的"冷却"时段内发生的后续事件.
开箱即用的Throttle/Buffer方法仅在超时过后响应,这不是我需要的.
下面是一些设置场景的代码,并使用Throttle(这不是我想要的解决方案):
class Program
{
static Stopwatch sw = new Stopwatch();
static void Main(string[] args)
{
var subject = new Subject<int>();
var timeout = TimeSpan.FromMilliseconds(500);
subject
.Throttle(timeout)
.Subscribe(DoStuff);
var factory = new TaskFactory();
sw.Start();
factory.StartNew(() =>
{
Console.WriteLine("Batch 1 (no delay)");
subject.OnNext(1);
});
factory.StartNewDelayed(1000, () =>
{
Console.WriteLine("Batch 2 (1s delay)");
subject.OnNext(2);
});
factory.StartNewDelayed(1300, () =>
{
Console.WriteLine("Batch 3 (1.3s delay)");
subject.OnNext(3);
});
factory.StartNewDelayed(1600, () =>
{
Console.WriteLine("Batch 4 (1.6s delay)");
subject.OnNext(4);
});
Console.ReadKey();
sw.Stop();
}
private static void …Run Code Online (Sandbox Code Playgroud) 我们的应用程序使用TPL来序列化(可能)长时间运行的工作单元.工作(任务)的创建是用户驱动的,可以随时取消.为了拥有响应式用户界面,如果不再需要当前的工作,我们想放弃我们正在做的事情,并立即开始另一项任务.
任务排队等同于:
private Task workQueue;
private void DoWorkAsync
(Action<WorkCompletedEventArgs> callback, CancellationToken token)
{
if (workQueue == null)
{
workQueue = Task.Factory.StartWork
(() => DoWork(callback, token), token);
}
else
{
workQueue.ContinueWork(t => DoWork(callback, token), token);
}
}
Run Code Online (Sandbox Code Playgroud)
该DoWork方法包含长时间运行的呼叫,因此,token.IsCancellationRequested如果/当检测到取消时,它不会像不断检查状态和挽救一样简单.长时间运行的工作将阻止任务继续,直到它完成,即使任务被取消.
我已经提出了两个样本方法来解决这个问题,但我不相信这两种方法都是正确的.我创建了简单的控制台应用程序来演示它们如何工
需要注意的重要一点是,在原始任务完成之前会继续触发.
尝试#1:内部任务
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
var token = cts.Token;
token.Register(() => Console.WriteLine("Token cancelled"));
// Initial work
var t = Task.Factory.StartNew(() =>
{
Console.WriteLine("Doing work");
// Wrap the long running …Run Code Online (Sandbox Code Playgroud) 我正在为我的应用程序的"粘合"层编写单元测试,并且很难为异步方法创建确定性测试,允许用户过早地取消操作.
具体来说,在一些异步方法中,我们有代码响应取消调用并确保对象在完成之前处于正确状态.我想确保测试涵盖这些代码路径.
在此场景中举例说明典型异步方法的一些C#伪代码如下:
public void FooAsync(CancellationToken token, Action<FooCompletedEventArgs> callback)
{
if (token.IsCancellationRequested) DoSomeCleanup0();
// Call the four helper methods, checking for cancellations in between each
Exception encounteredException;
try
{
MyDependency.DoExpensiveStuff1();
if (token.IsCancellationRequested) DoSomeCleanup1();
MyDependency.DoExpensiveStuff2();
if (token.IsCancellationRequested) DoSomeCleanup2();
MyDependency.DoExpensiveStuff3();
if (token.IsCancellationRequested) DoSomeCleanup3();
MyDependency.DoExpensiveStuff4();
if (token.IsCancellationRequested) DoSomeCleanup4();
}
catch (Exception e)
{
encounteredException = e;
}
if (!token.IsCancellationRequested)
{
var args = new FooCompletedEventArgs(a bunch of params);
callback(args);
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,我提出的解决方案涉及模拟MyDependency由胶层包裹的基础操作,并强制每个人在任意时间段内休眠.然后我调用异步方法,并告诉我的单元测试在取消异步请求之前休眠几毫秒.
像这样的东西(以Rhino Mocks为例):
[TestMethod]
public void FooAsyncTest_CancelAfter2()
{
// arrange …Run Code Online (Sandbox Code Playgroud) 我在使用泛型设置继承时遇到问题.
本质上我想要做的是拥有一个本身接受接口的通用接口.棘手的部分是"内部"界面可以在其上面分层更具体的界面.
以下是我正在尝试构建的结构的代表性示例:
public interface IThing { }
public interface IMoreSpecificThing : IThing { }
public interface IWidget<T> where T : IThing { }
public class MySpecificThing : IMoreSpecificThing { }
public class MyWidget : IWidget<MySpecificThing> { }
public class MyClass
{
public IWidget<IThing> MyProperty { get; set; }
public MyClass()
{
MyProperty = new MyWidget();
}
}
Run Code Online (Sandbox Code Playgroud)
问题是,当我将MyWidget分配给MyProperty时,我收到以下错误:
无法隐式转换
'MyWidget'为'IWidget<IThing>'.存在显式转换(您是否错过了演员?)
我做错了什么,有没有办法正确地做到这一点?
Reactive Extensions有一个性感的小钩子来简化调用异步方法:
var func = Observable.FromAsyncPattern<InType, OutType>(
myWcfService.BeginDoStuff,
myWcfService.EndDoStuff);
func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));
Run Code Online (Sandbox Code Playgroud)
我在WPF项目中使用它,它在运行时运行良好.
不幸的是,当尝试使用这种技术的单元测试方法时,我遇到了随机故障.包含此代码的测试的每五次执行中约有3次失败.
这是一个示例测试(使用Rhino/unity自动模拟容器实现):
[TestMethod()]
public void SomeTest()
{
// arrange
var container = GetAutoMockingContainer();
container.Resolve<IMyWcfServiceClient>()
.Expect(x => x.BeginDoStuff(null, null, null))
.IgnoreArguments()
.Do(
new Func<Specification, AsyncCallback, object, IAsyncResult>((inData, asyncCallback, state) =>
{
return new CompletedAsyncResult(asyncCallback, state);
}));
container.Resolve<IRepositoryServiceClient>()
.Expect(x => x.EndDoStuff(null))
.IgnoreArguments()
.Do(
new Func<IAsyncResult, OutData>((ar) =>
{
return someMockData;
}));
// act
var target = CreateTestSubject(container);
target.DoMethodThatInvokesService();
// Run the dispatcher for everything over background priority
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new …Run Code Online (Sandbox Code Playgroud) c# ×4
unit-testing ×2
.net ×1
asynchronous ×1
cancellation ×1
generics ×1
inheritance ×1
mocking ×1
wpf ×1