C# 链接观察值和传递结果值
我想将以下代码转换为反应流(使用C#中的C# 链接观察值和传递结果值,c#,system.reactive,C#,System.reactive,我想将以下代码转换为反应流(使用C#中的System.reactive) Method1、Method2和Method3都是长时间运行的任务。调用Method3需要Method2的结果,但是Method1和Method2可以并行运行。如果result1==null可以使用null提前终止整个操作 通常,Method2的返回速度比Method1快,因此可以在Method1完成之前启动Method3 var result1=Method1(); if(result1==null)返回null; va
System.reactive
)
Method1
、Method2
和Method3
都是长时间运行的任务。调用Method3
需要Method2
的结果,但是Method1
和Method2
可以并行运行。如果result1==null
可以使用null
提前终止整个操作
通常,Method2
的返回速度比Method1
快,因此可以在Method1
完成之前启动Method3
var result1=Method1();
if(result1==null)返回null;
var result2=Method2();
字符串result3=null;
如果(result2!=null)
{
结果3=方法3(结果2);
}
var combinedResult=CreateResult(result1);
如果(result2!=null)
{
combinedResult.Attr2=结果2;
}
if(result3!=null)
{
combinedResult.Attr3=结果3;
}
我迷失在嵌套函数和泛型之间。以下代码无法编译,因为它在泛型和返回类型方面存在问题(尤其是嵌套的Select
不返回值,而是返回可观察的)
var observe1=observeable.Start(()=>Method1());
var observe2=observeable.Start(()=>Method2());
可观察的.Zip(可观察的1,可观察的2,(结果1,结果2)=>
{
如果(result2!=null)
{
var Observable 3=可观察的。开始(()=>Method3(result2));
返回observe3.Select(result3=>
{
返回超级组合器(result1、result2、result3);
};
}
返回超级组合器(result1,null,null);
};
我对您的草稿做了一些改进,现在它可以按照您的描述工作了:
var stream1 = Observable.Start(Func1);
var stream2 = Observable.Start(Func2);
Observable.Zip(stream1, stream2, (res1, res2) =>
{
if (res1 == null)
return Observable.Start(() => new string[] { null });
if (res2 == null)
return Observable.Start(() => new string[] { res1, null });
return Observable.Start(() => Func3(res2)).Select(res3 => new[] { res1, res2, res3 });
})
.Merge()
.Subscribe(result =>
{
// 'result' is an array
// result[0] - result of Func1
// result[1] - result of Func2
// result[2] - result of Func3
// result.Length == 1 - means that Func1 returned 'null'
// result.Length == 2 - means that Func2 returned 'null'
});
但它不是一种真正的“反应式”方式,因为它包含命令语句(例如if
运算符)。以下是使其工作所需的:
var inner =
from m2 in Observable.Start(() => Method2())
from m3 in Observable.Start(() => Method3(m2))
select new { m2, m3 };
var query =
Observable
.Start(() => Method1())
.Publish(m1s =>
m1s
.Zip(
inner.TakeUntil(m1s.Where(m1 => m1 == null)),
(m1, m23) => new { m1, m23.m2, m23.m3 }))
.Where(x => x.m1 != null);
我已使用以下代码对此进行了测试:
public string Method1()
{
Console.WriteLine("Method1 Start");
Thread.Sleep(TimeSpan.FromSeconds(2.0));
Console.WriteLine("Method1 End");
return null; //"1";
}
public string Method2()
{
Console.WriteLine("Method2 Start");
Thread.Sleep(TimeSpan.FromSeconds(3.0));
Console.WriteLine("Method2 End");
return "2";
}
public string Method3(string x)
{
Console.WriteLine("Method3 Start");
Thread.Sleep(TimeSpan.FromSeconds(2.0));
Console.WriteLine("Method3 End");
return $"3-{x}";
}
只有当Method1
返回非空值时,查询才会生成一个值,否则它将在不生成值的情况下完成
Method3
在Method2
完成后立即执行,除非Method1
已返回null
,在这种情况下,不会执行Method3
这是您所要求的计算效率最高的实现。谢谢,您的解决方案正在运行。我只需将订阅
替换为第一异步
,这样我就可以等待
并回到同步世界。这里效率低下。方法3
等待,直到两个方法都出现1
和Method2
已完成。OP要求“Method3
可以在Method1
完成之前启动”,但这在该代码中不会发生。我同意:上面的代码是“Method1和Method2
并行的,只有这样才会调用Method3
”.与此同时,我还实施了一个定期“任务”解决方案,这正是我想要的。但我仍然希望找到“理想”反应式解决方案-由于给定的约束条件,我知道这不是真正的“反应式”。我同意您的代码比我的代码效率高一点。但是您关于方法3的陈述是不正确的。方法3在任何情况下都将被执行。更重要的是,即使查询
通知它已完成,它仍将执行到最后因为没有人阻止它。如果我们谈论的是效率,那么在这种特殊情况下,“任务+取消令牌”而不是“可观察的”将是最有效的方式。@VitaliiIlchenko-你是说Method3
将在我的代码中执行吗?@Vitaliiiilchenko-aCancellationToken
只会更有效nt如果方法支持合作取消。是的,它会。只需为Thread.Sleep设置另一个延迟,您就会看到。例如:Method1 Sleep-3秒、Method2 Sleep-2秒和Method3 Sleep-4秒。如果有这样的延迟,Method3将始终被调用。由于它被调用,它将被执行到最后。