1

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

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 关键字一起使用。
4

1 回答 1

1

不受 IO 限制的长时间运行的同步操作不属于 ThreadPool。在 ThreadPool 中运行此类操作会使您面临饥饿的风险,在这种情况下,线程池无法以足够快的速度启动线程来响应依赖它的许多其他 API 的需求。

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

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

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

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

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

于 2012-10-18T14:13:45.103 回答