异步/等待或任务.在控制台应用程序/Windows服务中运行 [英] Async/Await or Task.Run in Console Application/Windows Service

查看:41
本文介绍了异步/等待或任务.在控制台应用程序/Windows服务中运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在研究(包括查看有关此主题的所有其他SO帖子)实现(最可能)Windows Service worker的最佳方法,该工作将从数据库中提取工作项并在在后台执行一劳永逸"操作(所有工作项管理都将以异步方法进行处理).工作项将是Web服务调用和数据库查询.这些工作项的生产者将受到一些限制,以确保某种可衡量的方法来安排工作.下面的示例非常基础,仅用来突出while循环和for循环的逻辑.哪种方法比较理想或没关系?有没有更合适/更有效的方法来实现这一目标?

I have been researching (including looking at all other SO posts on this topic) the best way to implement a (most likely) Windows Service worker that will pull items of work from a database and process them in parallel asynchronously in a 'fire-and-forget' manner in the background (the work item management will all be handled in the asynchronous method). The work items will be web service calls and database queries. There will be some throttling applied to the producer of these work items to ensure some kind of measured approach to scheduling the work. The examples below are very basic and are just there to highlight the logic of the while loop and for loop in place. Which is the ideal method or does it not matter? Is there a more appropriate/performant way of achieving this?

异步/等待...

    private static int counter = 1;

    static void Main(string[] args)
    {
        Console.Title = "Async";

        Task.Run(() => AsyncMain());

        Console.ReadLine();            
    }

    private static async void AsyncMain()
    {
        while (true)
        {
            // Imagine calling a database to get some work items to do, in this case 5 dummy items
            for (int i = 0; i < 5; i++)
            {
                var x = DoSomethingAsync(counter.ToString());

                counter++;
                Thread.Sleep(50);
            }

            Thread.Sleep(1000);
        }
    }

    private static async Task<string> DoSomethingAsync(string jobNumber)
    {
        try
        {
            // Simulated mostly IO work - some could be long running
            await Task.Delay(5000);
            Console.WriteLine(jobNumber);
        }
        catch (Exception ex)
        {
            LogException(ex);
        }

        Log("job {0} has completed", jobNumber);

        return "fire and forget so not really interested";
    }

Task.Run ...

Task.Run...

    private static int counter = 1;

    static void Main(string[] args)
    {
        Console.Title = "Task";

        while (true)
        {
            // Imagine calling a database to get some work items to do, in this case 5 dummy items
            for (int i = 0; i < 5; i++)
            {
                var x = Task.Run(() => { DoSomethingAsync(counter.ToString()); });

                counter++;
                Thread.Sleep(50);
            }

            Thread.Sleep(1000);
        }
    }

    private static string DoSomethingAsync(string jobNumber)
    {
        try
        {
            // Simulated mostly IO work - some could be long running
            Task.Delay(5000);
            Console.WriteLine(jobNumber);
        }
        catch (Exception ex)
        {
            LogException(ex);
        }

        Log("job {0} has completed", jobNumber);

        return "fire and forget so not really interested";
    }

推荐答案

从数据库中提取工作项,并在后台以一劳永逸"的方式异步地并行处理它们

pull items of work from a database and process them in parallel asynchronously in a 'fire-and-forget' manner in the background

从技术上讲,您需要并发.您是否要异步并发并行并发还有待观察...

Technically, you want concurrency. Whether you want asynchronous concurrency or parallel concurrency remains to be seen...

工作项将是Web服务调用和数据库查询.

The work items will be web service calls and database queries.

这项工作是受I/O约束的,这意味着异步并发是更自然的方法.

The work is I/O-bound, so that implies asynchronous concurrency as the more natural approach.

这些工作项的生产者将受到一些限制,以确保采用某种可衡量的方法来安排工作.

There will be some throttling applied to the producer of these work items to ensure some kind of measured approach to scheduling the work.

此处暗示了生产者/消费者队列的想法.那是一种选择. TPL Dataflow提供了一些不错的生产者/消费者队列,这些队列异步兼容并支持限制.

The idea of a producer/consumer queue is implied here. That's one option. TPL Dataflow provides some nice producer/consumer queues that are async-compatible and support throttling.

或者,您可以自己进行节流.对于异步代码,有一个称为SemaphoreSlim的内置限制机制.

Alternatively, you can do the throttling yourself. For asynchronous code, there's a built-in throttling mechanism called SemaphoreSlim.

具有限制的TPL数据流方法:

TPL Dataflow approach, with throttling:

private static int counter = 1;

static void Main(string[] args)
{
    Console.Title = "Async";
    var x = Task.Run(() => MainAsync());
    Console.ReadLine();          
}

private static async Task MainAsync()
{
  var blockOptions = new ExecutionDataflowBlockOptions
  {
    MaxDegreeOfParallelism = 7
  };
  var block = new ActionBlock<string>(DoSomethingAsync, blockOptions);
  while (true)
  {
    var dbData = await ...; // Imagine calling a database to get some work items to do, in this case 5 dummy items
    for (int i = 0; i < 5; i++)
    {
      block.Post(counter.ToString());
      counter++;
      Thread.Sleep(50);
    }
    Thread.Sleep(1000);
  }
}

private static async Task DoSomethingAsync(string jobNumber)
{
  try
  {
    // Simulated mostly IO work - some could be long running
    await Task.Delay(5000);
    Console.WriteLine(jobNumber);
  }
  catch (Exception ex)
  {
    LogException(ex);
  }
  Log("job {0} has completed", jobNumber);
}


具有手动限制的异步并发方法:


Asynchronous concurrency approach with manual throttling:

private static int counter = 1;
private static SemaphoreSlim semaphore = new SemaphoreSlim(7);

static void Main(string[] args)
{
    Console.Title = "Async";
    var x = Task.Run(() => MainAsync());
    Console.ReadLine();          
}

private static async Task MainAsync()
{
  while (true)
  {
    var dbData = await ...; // Imagine calling a database to get some work items to do, in this case 5 dummy items
    for (int i = 0; i < 5; i++)
    {
      var x = DoSomethingAsync(counter.ToString());
      counter++;
      Thread.Sleep(50);
    }
    Thread.Sleep(1000);
  }
}

private static async Task DoSomethingAsync(string jobNumber)
{
  await semaphore.WaitAsync();
  try
  {
    try
    {
      // Simulated mostly IO work - some could be long running
      await Task.Delay(5000);
      Console.WriteLine(jobNumber);
    }
    catch (Exception ex)
    {
      LogException(ex);
    }
    Log("job {0} has completed", jobNumber);
  }
  finally
  {
    semaphore.Release();
  }
}


最后一点,我几乎不建议在SO上我自己的书,但是我认为这样做会真的使您受益.尤其是第8.10节(阻止/异步队列),第11.5节(限制)和第4.4节(限制数据流块).


As a final note, I hardly ever recommend my own book on SO, but I do think it would really benefit you. In particular, sections 8.10 (Blocking/Asynchronous Queues), 11.5 (Throttling), and 4.4 (Throttling Dataflow Blocks).

这篇关于异步/等待或任务.在控制台应用程序/Windows服务中运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆