循环运行任务

时间:2011-04-28 20:43:10

标签: c# multithreading

我有一个可能需要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使用的任务?

5 个答案:

答案 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)
}