【问题标题】:Async WCF call taking more time then Sync call异步 WCF 调用比同步调用花费更多时间
【发布时间】:2014-03-06 09:39:48
【问题描述】:

我们有 Windows 服务,它从 MSMQ 读取消息并一一处理。为了提高性能/吞吐量,我们转移到了 TPL。在处理消息时,我们有几个数据库调用和 WCF 调用。我们观察到,在进行 WCF 调用时,前几次调用速度更快,但后续调用需要更长的时间。这是示例代码。

public class TestWcf
{
    public void Process(string option)
    {
        // container for perellel tasks and WaitAll after 50 count
        TaskContainer batch = new TaskContainer(50, true);

        for (int i = 1; i <= 100; i++)
        {
            if(option.Equals("s"))
            {
                batch.Add(ProcessMessage);
            }
            else
            {
                batch.Add(ProcessMessageAsync);
            }
        }
    }

    public void ProcessMessage()
    {
        Stopwatch sw = Stopwatch.StartNew();

        PostingServiceClient client = new PostingServiceClient();
        client.UpdateArchiveStatus(Guid.Parse("B6D5C77C-330A-4B00-96BF-E91A2B5970E3"),"1234567890");

        sw.Stop();
        Console.WriteLine("Thread {0}, Elapsed Time: {1} s", Thread.CurrentThread.ManagedThreadId, sw.Elapsed.Seconds);
        //client.Close();
    }

    public void ProcessMessageAsync()
    {
        Stopwatch sw = Stopwatch.StartNew();

        PostingServiceClient client = new PostingServiceClient();
        var task = Task.Factory.FromAsync(
            client.BeginUpdateArchiveStatus(Guid.Parse("B6D5C77C-330A-4B00-96BF-E91A2B5970E3"), "1234567890", null,
                null), client.EndUpdateArchiveStatus);
        task.Wait();

        sw.Stop();
        Console.WriteLine("Thread {0}, Elapsed Time: {1} s", Thread.CurrentThread.ManagedThreadId, sw.Elapsed.Seconds);
        //client.Close();
    }

}

我们正在使用外部团队 WCF,它托管在 IIS 服务器中,应用程序池面向 .Net 2.0。 WCF 服务端,serviceThrottling 为 500,并且服务合同上没有定义 ServiceBehavior 属性。我们使用两个控制台实例同时运行上述程序,一个带有 Sync 选项,第二个带有 Async 选项。同步选项的完成速度比异步选项快得多。我的假设是,系统在执行异步操作时不会从 ThreadPool 释放任务。问题是,哪种方法更好(同步与异步 WCF 调用),或者在这种情况下是否有任何不同的方法来处理 WCF 调用。

【问题讨论】:

  • WCF 服务端的UpdateArchiveStatus 内部发生了什么?
  • 它正在使用 SharePoint API 来更新 SharePoint 列表。

标签: wcf asynchronous task-parallel-library


【解决方案1】:

在我开始讨论之前,您为 Windows 服务使用什么框架? .Net 4.5(或更高版本)还是 .Net 4.0?

我问的原因是因为如果你有async/await(你需要.Net 4.5.for1)那么它可以极大地改变你的代码的简单性。

现在让我们开始谈正事。因此,与您所做的事情有些不一致。

TaskContainer 到底是什么?我找不到任何关于它的文档。

这个代码块

var task = Task.Factory.FromAsync(
            client.BeginUpdateArchiveStatus(Guid.Parse("B6D5C77C-330A- 4B00-96BF-E91A2B5970E3"),"1234567890", null, null), 
            client.EndUpdateArchiveStatus);

task.Wait();

不是异步的。

它是同步的,由于task.Wait() 调用而使用任务。使用Task 会有一定的开销,但在大多数情况下,异步和可扩展性的好处将超过这个小开销。那一点点开销将比进行阻塞调用或阻塞线程来执行网络调用要好。

如果你想要一个真正的异步调用,你需要这样的东西:

var guid = Guid.Parse("B6D5C77C-330A-4B00-96BF-E91A2B5970E3");

// Since I don't have a copy of the operation contract,
// I can't tell if the client call returns a specific type,
// so I'm going to assume it's `void`
var task = Task.Factory.FromAsync(client.BeginUpdateArchiveStatus, client.EndUpdateArchiveStatus, guid, null);

// If you're using .Net 4.5 or higher
// Another side note is that you'll want to return `async Task` instead of `async void` if you decide/can use `await`
await task;
sw.Stop();

// If you're not using .Net 4.0
task.ContinueWith(t =>
  {
   sw.Stop();
   // Whatever code you want to execute now.
  });

解决您的假设:

我的假设,系统在执行异步操作时没有从 ThreadPool 释放任务。

Task.Factory.FromAsync 与 真正 异步调用一起使用时不会阻塞 ThreadPool 线程。 (有大量资源可以验证这一点,但here's 是我很快就提出来的。)

问题是,哪种方法更好(同步与异步 WCF 调用)或者在这种情况下是否有任何不同的方法来处理 WCF 调用。

最好的办法是正确实现异步任务。


[1]:如果你有 VS 2012 或更高版本,你可以拉入 NuGet 包 Microsoft.Bcl.Async 并为 .Net 4.0 解决这个问题。

编辑:我刚刚意识到我遇到了一个超过 1 年的问题。哎呀!这就是我略过未回答的问题所得到的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 2019-02-28
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    相关资源
    最近更新 更多