如何延迟像QTimer :: singleShot这样的C#Action?

时间:2018-11-15 11:48:57

标签: c#

Qt具有完善的功能,可以对Lambda进行定时操作。

可以在延迟后使用一行代码来执行操作:

String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference uidRef = rootRef.child(uid);
Map<String, Object> map = new HashMap<>();
map.put("lat", 41.890362);
map.put("long", 12.492263);
uidRef.updateChildren(map);

尽管我还没有在C#中找到等效项。


离我最近的是

    QTimer::singleShot(10, [=](){
        // do some stuff
    });

但这远非(从视觉上)干净。

是否有更好的方法来实现这一目标?

用例是通过串行线路将数据发送到某些硬件,单击按钮或执行操作时,通常需要发送命令和几毫秒后发送一个数据包。


具有辅助功能的解决方案:

Timer timer = new Timer();
timer.Interval = 10;
timer.Elapsed += (tsender, args) => { 
  // do some stuff 
  timer.Stop();
};
timer.Start();

呼叫者

    public void DelayTask(int timeMs, Action lambda)
    {
        System.Timers.Timer timer = new System.Timers.Timer();
        timer.Interval = timeMs;
        timer.Elapsed += (tsender, args) => { lambda.Invoke(); };
        timer.AutoReset = false;
        timer.Start();
    }

3 个答案:

答案 0 :(得分:5)

我想到的最接近的东西是像您建议的助手功能之类的东西:

perform_create

该类的用法将接近您对Qt的了解:

public static class DelayedAction
{
    public static Task RunAsync(TimeSpan delay, Action action)
    {
       return Task.Delay(delay).ContinueWith(t => action(), TaskScheduler.FromCurrentSynchronizationContext());
    }
}

更新

如现有SO question中所述,await DelayedAction.RunAsync(TimeSpan.FromSeconds(10), () => /* do stuff */); 默认情况下不保留同步上下文。

在当前问题中,lambda正在更新某些UI控件,因此,必须必须在UI线程上运行。

为此,调度程序必须在调用方法ContinueWithContinueWith)时指定同步上下文,以确保可以进行这种更新。

答案 1 :(得分:4)

您应该使用System.Threading.Timer而不是System.Timers.Timer。 System.Timers.Timer是多线程计时器,旨在与桌面应用程序一起使用,因此它继承自Component并需要通过属性进行配置。

尽管您可以使用System.Threading.Timer通过一个构造函数调用来创建一个单一触发计时器:

var timer= new Timer(_=>lambda(),null,timeMS,Timeout.Infinite);

这个快速又肮脏的控制台应用程序:

static void Main(string[] args)
{
    var timeMS = 1000;
    var timer = new Timer(_ => Console.WriteLine("Peekaboo"), null, timeMS, Timeout.Infinite);
    Console.ReadKey();
}

即使主线程被Peekaboo阻塞,一秒钟后仍将打印ReadKey();

答案 2 :(得分:2)

使用Microsoft的Reactive Framework(NuGet“ System.Reactive”),您可以执行以下操作:

IDisposable subscription =
    Observable
        .Timer(TimeSpan.FromMilliseconds(10.0))
        .Subscribe(_ => { /* Do Stuff Here */ });

IDisposable让您通过调用subscription.Dispose();取消订阅,然后再触发。