简体   繁体   English

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

[英]Run a continuous task from static main method using tasks?

I'm trying to introduce async/await into a new codebase to have it do a background task every x seconds. 我正在尝试将async / await引入新的代码库,以使其每x秒执行一次后台任务。

I've got the following, but it just stalls forever without ever running the action (printStuff). 我有以下内容,但它永远永久停止,而无需运行操作(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);
            }
        }
    }
}

How do I make sure that the tasks are running on the right threads, or even running at all? 如何确保任务在正确的线程上运行,甚至根本没有运行?

execute is expected to be a longish running database summary query. execute应该是运行时间较长的数据库摘要查询。 milliseconds is expected to be in the range of seconds to minutes, and would appreciate accuracy in timing. 毫秒预计在几秒到几分钟的范围内,并希望计时的准确性。

By 'right threads' I mean that the task should execute in an async/networking context, not synchronously with the main program dispatch. “正确的线程”是指任务应在异步/网络环境中执行,而不是与主程序分发同步。

However there may be times when it would be appropriate to run the task in a UI thread or a server thread if this code is to be reusable, and I would appreciate if it was explained what thread and how to configure it. 但是,如果可以重复使用此代码,有时可能需要在UI线程或服务器线程中运行任务,并且如果能解释该线程以及如何配置它,我将不胜感激。

You never actually start a task. 您实际上从未启动过任务。 Indeed, IMHO it does not make sense to represent execute as a task. 确实,恕我直言,将execute表示为任务没有任何意义。 Just pass the delegate itself: 只需传递代表本身:

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();
        }
    }
}

Note also that if you call Wait() , you'll never read the ReadLine() . 还要注意,如果调用Wait() ,则永远不会读取ReadLine() I've removed it in my version above. 我已在上面的版本中将其删除。

I've got the following, but it just stalls forever without ever running the action (printStuff). 我有以下内容,但它永远永久停止,而无需运行操作(printStuff)。

This happens because the Task you passed to executeEvety was created using the 'Task' constructor, so it will not be scheduled to run if you don't run it explicitly , so by awaiting you wait for an unstarted task to finish - this will never happend. 发生这种情况是因为您传递给executeEvety的Task是使用'Task'构造函数创建的,因此,如果您未显式运行它,则不会安排它运行,因此等待您等待未启动的任务完成-这永远不会发生。

You should instead pass an Action to executeEvery and invoke it every loop iteration without the need of async invocation: 相反,您应该将Action传递给executeEvery,并在每次循环迭代时都调用它,而无需异步调用:

ExecuteEvery(action, 5000).Wait();

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

If the action is long running and. 如果动作是长时间运行的。 you want to run it in the background without waiting for it to finish before the next iteration (fire and forget), add Task.Run when you invoke it: 如果您想在后台运行它,而不必等到下一次迭代(触发并忘记)之前完成, Task.Run在调用它时添加Task.Run

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

But wouldn't it be better to just forget about the endless loop and use Timer to achieve what you want? 但是,忘了无休止的循环并使用Timer实现您想要的效果会更好吗?

Creating a Task does not automatically start it. 创建Task不会自动启动它。 You're awaiting on a Task that will never complete because it is not started. 您正在等待一个由于尚未启动而永远不会完成的Task You should start a Task using Task.Run() I have modified your code a bit below: 您应该使用Task.Run()启动Task我在下面修改了您的代码:

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));
    }
}

I ended up fixing this by using a Func<Task> instead of a Action<Task> 我最终通过使用Func<Task>而不是Action<Task>

This enables the use of an async method, instead of a function, and allows the other method to be appropriately decorated with the async keyword. 这样就可以使用async方法而不是函数,并且可以使用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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM