为长同步操作创建一个包装器,以使用eap,apm模式和async / await

时间:2012-10-18 14:09:33

标签: c# task-parallel-library async-await

假设我有一项服务,它将进行长时间,昂贵的同步操作,即

class ExclamationMarkService
{
    public string GetData(string param)
    {
        Thread.Sleep(5000);
        return param + "!";
    } 
}

要通过EAP模式将其包装成异步,我可以这样做:

class ExclamationMarkServiceEAP
{
    public delegate void GetDataHandler(string data);
    public event GetDataHandler GetDataCompleted; 


    public void GetDataWorker(object param)
    {  
            var service = new ExclamationMarkService();
            string data = service.GetData((string)param); 

            if (GetDataCompleted != null)
            {
                GetDataCompleted(data);
            }
    }

    public void GetData(string param)
    {
        var thread = new Thread(GetDataWorker) {IsBackground = true};
        thread.Start(param);
    }
}

新的async / await运算符与此类似:

class ExclamationMarkServiceTaskAsync
{
    public async Task<string> GetDataAsync(string param)
    {
        var service = new ExclamationMarkService();
        return await Task.Run(() => service.GetData(param));
    }
}

用法:

        public static void CallExclamationMarkServiceEAP()
        {
            var service = new ExclamationMarkServiceEAP();
            service.GetDataCompleted += service_GetDataCompleted;
            service.GetData("hello EAP");
        }

        static void service_GetDataCompleted(string data)
        {
            Console.WriteLine(data);
        }

        public static async void CallExclamationMarkServiceTaskAsync()
        {
            var service = new ExclamationMarkServiceTaskAsync();
            var data = await service.GetDataAsync("hello TaskAsync");
            Console.WriteLine(data);
        }

        static void Main(string[] args)
        {
            CallExclamationMarkServiceEAP();
            CallExclamationMarkServiceTaskAsync();
            Console.Read();
        } 

在这两种情况下,我都设法将工作卸载到后台。在EAP的情况下,通过显式启动一个线程。对于async / await版本,请使用Task.Run。

问题:

  • 我的ExclamationMarkService的APM实现如何?
  • 同时给出EAP和APM版本,如何使用Task类的现有方法(Task.Factory.StartNew / Task.Factory.FromAsync等)将它们包装起来,以便它们可以与async / await一起使用关键字。

1 个答案:

答案 0 :(得分:1)

长时间运行的非IO绑定的同步操作不属于ThreadPool。在ThreadPool中运行此类操作会使您面临饥饿的风险,其中池不会足够快地启动线程以响应依赖它的许多其他API的需求。

如果你想运行一些冗长的东西,可以在自己的线程上运行它,如果需要在某个UI中显示,请小心将结果反馈回正确的上下文。

因此,您的第一种方法似乎更合适。

另一方面,TPL提供了提示任务长期运行的机会,并允许系统决定运行它的最佳位置。它很简单:

Task.Factory.StartNew(someSyncAction, TaskCreationOptions.LongRunning)

StartNew返回一个任务。你可以等待它。