我有一个可能需要5-60秒才能运行的功能,我需要每隔10秒运行一次,但它应该只在先前启动的功能运行完毕时启动,我现在的代码是
Action myAction = new Action(() =>
{
Debug.WriteLine("just testing");
Thread.Sleep(15000);
});
Task myTask = Task.Factory.StartNew(myAction, _cts.Token);
Timer myTimer = new Timer(state =>
{
if (myTask.IsCompleted)
{
myTask = Task.Factory.StartNew(myAction, _cts.Token);
}
}, null, 10000, 10000);
一切都运转正常,但我想知道我的问题是否有更好的解决方案?或者是否有可能不创建新任务(Task.Factory.StartNew)但只使用myTimer使用的任务?
答案 0 :(得分:4)
您可以使用ContinueWith():
Task.Factory.StartNew(myAction, _cts.Token).ContinueWith(_ => myAction);
查找它的重载,它有很多选项来控制运行延续的情况。
答案 1 :(得分:1)
有一个名为Quartz.net的开源任务调度程序。您可以在http://quartznet.sourceforge.net/
找到它它支持您提到的特定方案。它是一个非常强大的解决方案,具有良好的可扩展性。
答案 2 :(得分:1)
如果你喜欢冒险,另一种可能性就是使用Rx:
Observable.Timer(TimeSpan.FromSeconds(10)).TakeUntilCanceled(cancel).Subscribe(_ => myAction);
使用TakeUntilCanceled扩展程序:
public static class CancellationTokenXs
{
public static IObservable<T>
TakeUntilCanceled<T>(this IObservable<T> source, CancellationToken cancellationToken)
{
var subject = new Subject<Unit>();
cancellationToken.Register(() => subject.OnNext(new Unit()), true);
return source.TakeUntil(subject);
}
}
答案 3 :(得分:0)
更好的想法是,不是每隔10秒尝试调用一次,而是依赖于任务完成的回调,作为以下代码中的示例:
DateTime sinceExec = DateTime.Now;
BackgroundWorker bgWorker = new BackgroundWorker();
bgWorker.DoWork += (bgSender, bgArgs) =>
{
sinceExec = DateTime.Now;
Debug.WriteLine("Test!");
Thread.Sleep(5000);
};
bgWorker.RunWorkerCompleted += (bgSender, bgArgs) =>
{
// it didn't take 10000 milliseconds
if ((DateTime.Now - sinceExec).Milliseconds < 10000)
{
//Calculate time to wait
TimeSpan timeToWait = (DateTime.Now - sinceExec);
// wait that amount of time
Thread.Sleep(timeToWait);
}
//Re-execute the worker
bgWorker.RunWorkerAsync();
};
bgWorker.RunWorkerAsync();
BackgroundWorker类的功能使得在调用DoWork
时执行事件处理程序RunWorkerAsync()
,并在RunWorkerCompleted
完成时调用DoWork
。
答案 4 :(得分:-1)
您可以使用lock statement。锁定语句创建critical section,其中只有一个可以为给定对象一次运行。
使用主线程和任务线程可以访问的对象作为互斥锁。围绕任务函数的代码和使用lock语句启动任务的行将完成您的目标。任务函数将获取锁,并且在完成之前不会释放它,并且创建函数将在创建另一个任务之前等待获取锁。
Action myAction = new Action(() =>
{
lock(this)
{
Debug.WriteLine("just testing");
Thread.Sleep(15000);
}
});
在开启行动的代码中,
lock(myAction)
{
Task.Factory.StartNew(myAction, _cts.Token)
}