使用任务从静态主方法运行连续任务?

时间:2018-01-09 04:18:42

标签: c# async-await

我试图将async / await引入新的代码库,让它每隔x秒执行一次后台任务。

我已经得到了以下内容,但它只是在没有运行动作(printStuff)的情况下永远停止。

using System;
using System.Threading.Tasks;

namespace ASyncTaskDemo
{
    class Program3
    {    
        public static void Main(string[] args)
        {
            Action action = printStuff;    
            ExecuteEvery(new Task(action), 5000).Wait();
            //simulate the rest of the application stalling until shutdown events.
            Console.ReadLine();
        }

        private static int x = 0;

        private static void printStuff()
        {
            Console.Error.Write(x++);
        }

        private static async Task ExecuteEvery(Task execute, int milliseconds)
        {
            while (true)
            {
                var delay = Task.Delay(milliseconds);
                await Task.WhenAll(delay, execute);
            }
        }
    }
}

如何确保任务在正确的线程上运行,甚至可以运行?

执行应该是一个长期运行的数据库摘要查询。 毫秒预计在几秒到几分钟的范围内,并且会提高计时的准确性。

通过正确的线程'我的意思是任务应该在异步/网络环境中执行,而不是与主程序调度同步。

但是,如果要重用此代码,有时候在UI线程或服务器线程中运行任务是合适的,如果解释了什么线程以及如何配置它,我将不胜感激。< / p>

4 个答案:

答案 0 :(得分:2)

你永远不会真正开始任务。实际上,恕我直言,将execute表示为一项任务是没有意义的。只需传递代表本身:

class Program3
{    
    public static void Main(string[] args)
    {
        Action action = printStuff;    
        ExecuteEvery(action, 5000); // ignore returned task
        //simulate the rest of the application stalling until shutdown events.
        Console.ReadLine();
    }

    private static int x = 0;

    private static void printStuff()
    {
        Console.Error.Write(x++);
    }

    private static async Task ExecuteEvery(Action execute, int milliseconds)
    {
        while (true)
        {
            await Task.Delay(milliseconds);
            execute();
        }
    }
}

另请注意,如果您致电Wait(),则永远不会阅读ReadLine()。我在上面的版本中删除了它。

答案 1 :(得分:2)

  

我已经得到了以下内容,但它永远不会永远停滞   运行动作(printStuff)。

这是因为您传递给executeEvety的Task是使用&#39;任务&#39;创建的。构造函数,因此如果你没有明确地运行它,它就不会安排运行,所以等待你等待一个未启动的任务完成 - 这将永远不会发生。

您应该将Action传递给executeEvery并在每次循环迭代时调用它,而不需要异步调用:

ExecuteEvery(action, 5000).Wait();

private static async Task ExecuteEvery(Action action, int milliseconds)
{
    while (true)
    {
        await Task.Delay(milliseconds);
        action();
    }
}

如果动作是长时间运行的话。你想在后台运行它而不等待它在下一次迭代之前完成(火与忘记),当你调用它时添加Task.Run

private static async Task ExecuteEvery(Action action, int milliseconds)
{
    while (true)
    {
        await Task.Delay(milliseconds);
        Task.Run(() => action());
    }
}

但是,忘记无限循环并使用Timer来达到你想要的效果会不会更好?

答案 2 :(得分:1)

创建Task不会自动启动它。您正在等待永远无法完成的Task,因为它尚未启动。您应该使用Task开始Task.Run()我已经修改了您的代码:

public static void Main(string[] args)
{
    Action action = printStuff;
    ExecuteEvery(action, 5000).Wait();
    //simulate the rest of the application stalling until shutdown events.
    Console.ReadLine();
}

private static int x = 0;

private static void printStuff()
{
    Console.Error.Write(x++);
}

private static async Task ExecuteEvery(Action execute, int milliseconds)
{
    while (true)
    {
        var delay = Task.Delay(milliseconds);
        await Task.WhenAll(delay, Task.Run(execute));
    }
}

答案 3 :(得分:0)

我最后使用Func<Task>代替Action<Task>

来解决此问题

这允许使用异步方法而不是函数,并允许使用async关键字适当地修饰其他方法。

/// <summary>
/// Executes a task every x milliseconds, but will wait for long running tasks to complete.
/// </summary>
/// <param name="execute">A Task Producer</param>
/// <param name="milliseconds">How often to run execute</param>
/// <param name="cancel">A cancellation token</param>
/// <returns></returns>
private async Task ExecuteEvery(Func<Task> execute, int milliseconds, CancellationToken cancel)
{
    while (true)
    {
        var delay = Task.Delay(milliseconds, cancel);
        await Task.WhenAll(delay, execute());
        if(cancel.IsCancellationRequested) break;
    }
}