使用.NET Core进行异步编程?

时间:2016-08-11 22:03:44

标签: c# async-await asp.net-core task-parallel-library .net-core

我是.NET Core和异步编程的新手。我正在尝试实现一个控制台应用程序来执行以下操作:

  1. 控制台应用程序应该作为两个外部API之间的中介。例如。 API-1和API-2。

  2. 它应该在每10毫秒后调用API-1来获取数据。

  3. 立即调用API-2提交从API-1收到的数据。

  4. 控制台应用程序需要等待API-1接收数据,但不必等待API-2的响应。
  5. 以下是我的代码。它没有按预期工作。首先,它按预期在10毫秒内调用API-1,但之后仅在它收到API-2的响应后调用API-1。
    因此,假设API-2需要20秒,API-1也会在20秒后被调用。

    如何使API-2调用异步,因此不必等待API-2响应?

    namespace ConsoleApp1
    {
        public class Program
        {
            private static Timer _timer;
            private const int TIME_INTERVAL_IN_MILLISECONDS = 10; // 10 milliseconds
            private const int API2_DELAY = 20000; // 20 seconds
            public static void Main(string[] args)
            {
                Dowork().Wait();
                Console.WriteLine("Press Any Key to stop");
                Console.ReadKey();
                Console.WriteLine("Done");
            }
    
            private static async Task Dowork()
            {
                var data = new SomeData();
                _timer = new Timer(CallAPI1, data, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
                await Task.Yield();            
            }
    
            private static async void CallAPI1(object state)
            {           
                var data = state as SomeData;
    
                Console.WriteLine("Calling API One to get some data.");
                data.SomeStringValue = DateTime.Now.ToString();
    
                await CallAPI2(data);
                _timer.Change(TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
            }
    
            private static async Task CallAPI2(SomeData data)
            {           
                Console.WriteLine("Calling API Two by passing some data received from API One " + data.SomeStringValue);
    
                // the delay represent long running call to API 2
                await Task.Delay(API2_DELAY);
            }
        }
    }
    

    POCO课程

    namespace ConsoleApp1
    {
        public class SomeData
        {
            public string SomeStringValue { get; set; }
        }
    }
    

    另请注意,API-1和API-2将在ASP.NET Core 1

    中开发

    UPDATE1
    让我重新说一句。 API-1将在.Net核心中开发,但API-2将是Windows工作流服务。这意味着我们可以多次调用WF。 WF将持续保留请求并一次处理一个。

    UPDATE2
    完成所有答案和提供的链接后。我想使用Windows服务作为中介而不是控制台应用程序。现在.Net core不支持窗口服务,但是这个nuget-package可以在Windows服务中托管.Net核心,或者我可以使用4.6.2的经典Windows服务。我想我也可以在Windows服务中做异常的实现。

2 个答案:

答案 0 :(得分:2)

在这种情况下,我会做很多不同的事情。我也不会使用计时器,而是使用Task.Delay,在尝试向其投入更多数据之前,我肯定会等待API2完成。此外,我会确保我的async方法返回TaskTask<T>,请注意您的CallAPI1来电不是,我理解它是一个定时器回调 - 但这是另一个问题。

请考虑以下事项:

async Task IntermediateAsync()
{
    Console.WriteLine("Press ESC to exit...");

    while (Console.ReadKey(true).Key != ConsoleKey.Escape)
    {
        var result = await _apiServiceOne.GetAsync();
        await _apiServiceTwo.PostAsync(result);

        // Wait ten milliseconds after each successful mediation phase
        await Task.Delay(10);
    }
}

这将采取以下方式:

  1. 打印一条指示用户如何退出
  2. 的行
  3. 开始循环
  4. 获取API1的结果
  5. 将结果传递给API2
  6. 等待10毫秒
  7. [第2步]
  8. 最后,无论您是否使用.NET Core,这都是相同的建议。任何API交互都应遵循相同的准则。

    备注

    在第二次API调用中使用“即发即忘”只是将代码设置为失败。由于它是一个API调用,因此I/O绑定操作可能会出现一些延迟,并且应该假设10毫秒的紧密循环只会泛滥该端点上的可用性。为什么不等到它完成,你可能有什么理由?

答案 1 :(得分:0)

调用API2时删除await

    private static async void CallAPI1(object state)
    {
        var data = state as SomeData;

        Console.WriteLine("Calling API One to get some data.");
        data.SomeStringValue = DateTime.Now.ToString();
        //Before this will cause the program to wait
        await CallAPI2(data);
        // Now it will call and forget
        CallAPI2(data);
        _timer.Change(TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
    }

修改:

大卫指出,当然有很多方法可以解决这个问题。这不是解决问题的正确方法。

另一种做法是使用quartz.net

  1. 将API1安排为重复工作
  2. 完成API1后,安排另一个作业将API2作为独立作业运行
  3. 这样,当API2失败时,您可以重播/重复该作业。