lob*_*ism 6 c# f# asynchronous task-parallel-library system.reactive
我有一个应用程序必须执行以下类型的操作,最好是在GUI线程上,因为这是大多数操作发生的地方,并且没有长时间运行的操作:
Wait 1000
FuncA()
Wait 2000
FuncB()
Wait 1000
FuncC()
Run Code Online (Sandbox Code Playgroud)
我意识到我可以使用具有状态机样式OnTick功能的计时器,但这似乎很麻烦:
int _state;
void OnTick(object sender, EventArgs e) {
switch (_state) {
case 0:
FuncA();
_timer.Interval = TimeSpan.FromSeconds(2);
_state = 1;
break;
case 1:
FuncB();
_timer.Interval = TimeSpan.FromSeconds(1);
_state = 2;
break;
case 2:
FuncC();
_timer.IsEnabled = false;
_state = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
另外,我希望能够使它足够通用,以便做类似的事情
RunSequenceOnGuiThread(new Sequence {
{1000, FuncA}
{2000, FuncB}
{1000, FuncC}};
Run Code Online (Sandbox Code Playgroud)
有没有惯用的方法来做这种事情?鉴于所有的TPL内容,或者Rx,甚至F#中的计算表达式,我都假设存在,但我找不到它.
Ana*_*tts 10
Observable.Concat(
Observer.Timer(1000).Select(_ => Func1()),
Observer.Timer(2000).Select(_ => Func2()),
Observer.Timer(1000).Select(_ => Func3()))
.Repeat()
.Subscribe();
Run Code Online (Sandbox Code Playgroud)
要做到这一点,你唯一需要做的就是确保你的Func返回一个值(即使该值是Unit.Default
,即没有)
编辑:以下是如何制作通用版本:
IObservable<Unit> CreateRepeatingTimerSequence(IEnumerable<Tuple<int, Func<Unit>>> actions)
{
return Observable.Concat(
actions.Select(x =>
Observable.Timer(x.Item1).Select(_ => x.Item2())))
.Repeat();
}
Run Code Online (Sandbox Code Playgroud)
这是F#中的这个草图:
let f() = printfn "f"
let g() = printfn "g"
let h() = printfn "h"
let ops = [
1000, f
2000, g
1000, h
]
let runOps ops =
async {
for time, op in ops do
do! Async.Sleep(time)
op()
} |> Async.StartImmediate
runOps ops
System.Console.ReadKey() |> ignore
Run Code Online (Sandbox Code Playgroud)
这是在控制台应用程序中,但您可以在GUI线程上调用runOps.另见此博客.
如果您使用VS11/NetFx45/C#5,你可以做类似的事情用C#async
/ await
和List
的Tuple
的Action
代表.
使用异步CTP或.NET 4.5(C#5),使用异步方法和await运算符非常容易.这可以直接在UI线程上调用,它将按预期工作.
public async void ExecuteStuff()
{
await TaskEx.Delay(1000);
FuncA();
await TaskEx.Delay(2000);
FuncB();
await TaskEx.Delay(1000);
FuncC();
}
Run Code Online (Sandbox Code Playgroud)
这是一种将“收益回报”和反应式框架结合起来的方法,为您提供“穷人的异步”。基本上让你“等待”任何 IObservable。在这里,我只是将它用于计时器,因为这是您感兴趣的,但您也可以让它“等待”按钮单击(使用 )Subject<Unit>
等,然后再继续下一步。
public sealed partial class Form1 : Form {
readonly Executor _executor = new Executor();
public Form1() {
InitializeComponent();
_executor.Run(CreateAsyncHandler());
}
IEnumerable<IObservable<Unit>> CreateAsyncHandler() {
while (true) {
var i = 0;
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
}
}
IObservable<Unit> WaitTimer(double ms) {
return Observable.Timer(TimeSpan.FromMilliseconds(ms), new ControlScheduler(this)).Select(_ => Unit.Default);
}
}
public sealed class Executor {
IEnumerator<IObservable<Unit>> _observables;
IDisposable _subscription = new NullDisposable();
public void Run(IEnumerable<IObservable<Unit>> actions) {
_observables = (actions ?? new IObservable<Unit>[0]).Concat(new[] {Observable.Never<Unit>()}).GetEnumerator();
Continue();
}
void Continue() {
_subscription.Dispose();
_observables.MoveNext();
_subscription = _observables.Current.Subscribe(_ => Continue());
}
public void Stop() {
Run(null);
}
}
sealed class NullDisposable : IDisposable {
public void Dispose() {}
}
Run Code Online (Sandbox Code Playgroud)
这是对 Daniel Earwicker 的 AsyncIOPipe 想法的轻微修改:http://smellegantcode.wordpress.com/2008/12/05/asynchronous-sockets-with-yield-return-of-lambdas/