【问题标题】:Async WCF Service with multiple async calls inside内部具有多个异步调用的异步 WCF 服务
【发布时间】:2012-07-20 03:52:02
【问题描述】:

我在 WCF 中有一个 Web 服务,它消耗一些外部 Web 服务,所以我想做的是使这个服务异步以释放线程,等待所有外部服务完成,然后返回结果给客户。

使用框架 4.0

public class MyService : IMyService
{
    public IAsyncResult BeginDoWork(int count, AsyncCallback callback, object serviceState)
    {    
        var proxyOne = new Gateway.BackendOperation.BackendOperationOneSoapClient();
        var proxyTwo = new Gateway.BackendOperationTwo.OperationTwoSoapClient();

        var taskOne = Task<int>.Factory.FromAsync(proxyOne.BeginGetNumber, proxyOne.EndGetNumber, 10, serviceState);
        var taskTwo = Task<int>.Factory.FromAsync(proxyTwo.BeginGetNumber, proxyTwo.EndGetNumber, 10, serviceState);
        
        var tasks = new Queue<Task<int>>();
        tasks.Enqueue(taskOne);
        tasks.Enqueue(taskTwo);

        return Task.Factory.ContinueWhenAll(tasks.ToArray(), innerTasks =>
        {
            var tcs = new TaskCompletionSource<int>(serviceState);
            int sum = 0;

            foreach (var innerTask in innerTasks)
            {
                if (innerTask.IsFaulted)
                {
                    tcs.SetException(innerTask.Exception);
                    callback(tcs.Task);
                    return;
                }

                if (innerTask.IsCompleted)
                {
                    sum = innerTask.Result;
                }
            }

            tcs.SetResult(sum);

            callback(tcs.Task);
        });
    }

    public int EndDoWork(IAsyncResult result)
    {
        try
        {
            return ((Task<int>)result).Result;
        }
        catch (AggregateException ex)
        {
            throw ex.InnerException;
        }

    }
}

我的问题是:

  1. 此代码使用三个线程:一个在 BeginDoWork,另一个在代码进入时被实例化的 匿名方法ContinueWhenAll里面,最后一个when 回调被执行,在本例中为 EndDoWork。这是正确的还是 我在通话中做错了什么?我应该使用任何 同步上下文?注意:同步上下文为空 在主线程上。

  2. 如果我在两个人之间“共享”信息会发生什么 线程,例如回调函数?这会导致 性能问题或匿名方法就像我的闭包 可以共享数据吗?

使用 Framework 4.5 和 Async and Await

现在有了 Framework 4.5,代码似乎比以前简单多了:

    public async Task<int> DoWorkAsync(int count)
    {
        var proxyOne = new Backend.ServiceOne.ServiceOneClient();
        var proxyTwo = new Backend.ServiceTwo.ServiceTwoClient();

        var doWorkOne = proxyOne.DoWorkAsync(count);
        var doWorkTwo = proxyTwo.DoWorkAsync(count);

        var result = await Task.WhenAll(doWorkOne, doWorkTwo);

        return doWorkOne.Result + doWorkTwo.Result;
    }

但是在这种情况下,当我调试应用程序时,我总是看到代码是在同一个线程上执行的。所以我的问题是:

3.. 当我在等待“可等待”代码时,该线程是否被释放并返回线程池执行更多请求?

3.1。如果是这样,我想当我从等待任务中获得结果时,执行会在之前调用的同一线程上完成。那可能吗?如果该线程正在处理另一个请求会怎样?

3.2 如果不是,如何释放线程以将其发送回具有 Asycn 和 Await 模式的线程池?

谢谢!

【问题讨论】:

  • FWIW,使用异步目标包,您可以将 async/await 用于 4.0 项目。
  • 4.0能否返回Task(即使它不使用async/await)?我想这会稍微简化代码。 Task 实现 IAsyncResult IIRC

标签: wcf asynchronous task-parallel-library async-await


【解决方案1】:

1。此代码使用三个线程:一个在 BeginDoWork 中实例化,另一个在代码进入匿名方法 ContinueWhenAll 时实例化,最后一个在执行回调时实例化,在本例中为 EndDoWork。这是正确的还是我在通话中做错了什么?我应该使用任何同步上下文吗?

最好从“任务”而不是“线程”的角度来思考。您在这里确实有三个任务,每个任务都将在线程池上运行,一次一个。

2。如果我在线程之间“共享”信息会发生什么,例如回调函数?这会导致性能问题还是匿名方法就像我可以共享数据的闭包?

您不必担心同步问题,因为这些任务中的每一个都不能同时运行。 BeginDoWork 在返回之前注册延续,所以当延续可以运行时它实际上已经完成。 EndDoWork 可能在继续完成之前不会被调用;但即使是,它也会阻塞,直到继续完成。

(从技术上讲,延续可以BeginDoWork 完成之前开始运行,但BeginDoWork 只是在那个时候返回,所以没关系)。

3。当我在等待“awaitable”代码时,该线程是否被释放并返回线程池执行更多请求?

是的。

3.1。如果是这样,我想当我从等待任务中获得结果时,执行会在之前调用的同一线程上完成。那可能吗?如果该线程正在处理另一个请求会怎样?

没有。您的主机(在本例中为 ASP.NET)可能会在它碰巧可用的任何线程上继续 async 方法。

这是非常安全的,因为一次只有一个线程在执行

附:我推荐

var result = await Task.WhenAll(doWorkOne, doWorkTwo);
return result[0] + result[1];

而不是

var result = await Task.WhenAll(doWorkOne, doWorkTwo);
return doWorkOne.Result + doWorkTwo.Result;

因为在async 编程中应该避免使用Task.Result

【讨论】:

  • 对于最后一段代码,只做 var result1 = await doWorkOne; 的优缺点是什么? var result2 = 等待 doWorkTwo;然后添加它们?避免“包装器”任务,尽管这可能可以忽略不计。
  • WRT '可以在任何线程上继续',我认为这不是真的,因为 asp.net 同步上下文并且没有使用 ConfigureAwait(false);?
  • @James: await doWorkOne(); await doWorkTwo()" approach starts One` 然后直到 One 完成后才会启动 TwoWhenAll(doWorkOne(), doWorkTwo()) 方法同时启动它们,然后在它们都完成时继续。所以这是一种简单的并行形式。
  • ASP.NET SynchronizationContext 表示 request 上下文。 async 方法在不同的线程上恢复是完全有效的,只要它具有正确的请求上下文。请求上下文一次只允许一个线程进入,但并不总是同一个线程。刚刚在async ASP.NET 上看到了great video - 他们在大约一个小时内几乎涵盖了所有内容!
  • @PeetBrits:它很容易导致deadlock scenario that I explain on my blog。另外,Result 表示您在进行异步编程;你首先失去了async 的所有好处。
猜你喜欢
  • 2012-10-03
  • 1970-01-01
  • 2016-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-04
  • 1970-01-01
相关资源
最近更新 更多