首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使[IsOneWay=true] WCF服务异步和使用客户端上的任务调用同步方法之间有显著差异吗?

使[IsOneWay=true] WCF服务异步和使用客户端上的任务调用同步方法之间有显著差异吗?
EN

Stack Overflow用户
提问于 2013-06-26 22:27:35
回答 2查看 3K关注 0票数 3

如果我有这样定义的服务:

代码语言:javascript
复制
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface IMyService
{
    [OperationContract(IsOneWay = true)]
    [ReceiveContextEnabled(ManualControl = true)]
    void DoSomething(Message<XElement> message);
}

我想从我的客户端异步调用它(使用共享契约,而不是从svcutil或添加服务引用生成),我可以这样做:

代码语言:javascript
复制
Task task = Task.Factory.StartNew(() => myService.DoSomething(message));

... some other code

task.Wait();

我也可以将我的服务定义为异步的:

代码语言:javascript
复制
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface ICacheKeyExchangeAsync
{
    [OperationContract(IsOneWay = true, AsyncPattern = true)]
    [ReceiveContextEnabled(ManualControl = true)]
    IAsyncResult BeginDoSomething(Message<XElement> message, AsyncCallback callback, object state);
    void EndDoSomething(IAsyncResult result);
}

并改为这样做

代码语言:javascript
复制
IAsyncResult result = myService.BeginDoSomething(message, null, null);

.... some other code

myService.EndDoSomething(result);

这两种方法之间有显著差异吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-06-29 21:29:04

是的,Thread Pool线程使用率存在差异。

CLR线程池将线程拆分为两种类型: worker和I/O (您可以在Simple description of worker and I/O threads in .NETMSDN上找到有关它们的更多信息)。一般而言,线程池为每个核心提供250个工作线程和1000个I/O线程,因此您可以使用工作线程来处理您的WCF服务输入,并使用I/O线程等待异步发送/接收操作完成(这是由overlapped I/O机制在Windows操作系统级别支持的)。

记住上面的内容,让我们通过使用ThreadPool.GetAvailableThreads()方法来看看这两种方法都使用了哪些线程:

代码语言:javascript
复制
 int worker;
 int ioCompletion;
 ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
 Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);

我将只展示客户端的线程池利用率结果,但对于服务器端也是如此。

用于单向操作的WCF方法。

对于WCF合同:

代码语言:javascript
复制
 [ServiceContract]
 public interface IService1
 {
     [OperationContract(IsOneWay = true, AsyncPattern = true)]
     IAsyncResult BeginDoSomething(int value, AsyncCallback callback, object state);

     void EndDoSomething(IAsyncResult result);
 }

让我们使用下面的代码将100个请求从客户端发送到服务器:

代码语言:javascript
复制
ChannelFactory<IService1> channelFactory = new ChannelFactory<IService1>();
var client = channelFactory.CreateChannel();

for (int i = 0; i < 100; i++)
{
    int worker;
    int ioCompletion;
    ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
    Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);

    client.BeginDoSomething(i, asyncCallback, null);
}

输出为:

代码语言:javascript
复制
1023 worker and 1000 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available

如您所见,我的x4核心机器上的所有工作线程都可用,并且正在使用几个I/O线程。

作为第三方任务运行同步单向操作的

对于WCF合同:

代码语言:javascript
复制
 [ServiceContract]
 public interface IService2
 {
     [OperationContract(IsOneWay = true)]
     void DoSomething(int value);
 }

让我们使用下面的代码运行100个从客户端到服务器的请求(只想注意到TPL在底层使用CLR ThreadPool ):

代码语言:javascript
复制
for (int i = 0; i < 100; i++)
{
    int worker;
    int ioCompletion;
    ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
    Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);

    Task.Run(() => client.DoSomething(i));
}

输出为:

代码语言:javascript
复制
1023 worker and 1000 I/O threads are available
1022 worker and 1000 I/O threads are available
1021 worker and 1000 I/O threads are available
1020 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available

正如您所看到的,现在正在利用工作线程,而不是I/O线程。

那么,推荐的方法是什么呢?

总而言之,您的解决方案应该:

bottlenecks;

  • Wrap features;

  • It's利用线程池中的工作线程和I/O线程(特别是对于高负载的应用程序)来防止
  • 中的C#异步操作,因此您可以获得TPL和新的TPL async/await TPL的所有好处,异步执行OneWay操作是绝对合理的(考虑到,有时实际上是OneWay is not OneWay )。

因此,推荐的方法是Task-based asynchronous pattern for WCF,它满足上述所有要求。

用于WCF.的基于任务的异步模式

对于合同:

代码语言:javascript
复制
[ServiceContract]
public interface IService3
{
    [OperationContract(IsOneWay = true)]
    Task DoSomethingAsync(int value);
}

让我们再次发送100个请求:

代码语言:javascript
复制
for (int i = 0; i < 100; i++)
{
     int worker;
     int ioCompletion;
     ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
     Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
     client.DoSomethingAsync(i);
}

输出:

代码语言:javascript
复制
1023 worker and 1000 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
票数 12
EN

Stack Overflow用户

发布于 2013-06-27 16:55:45

OneWay = true

如果使用OneWay属性,客户端将不会等待服务完成方法的执行。您可以通过创建一个除了等待之外什么都不做的服务方法来轻松地测试。客户端将调用服务方法(即使是同步的),然后继续执行。

您可以通过在您的服务中编写一个简单的测试方法来非常容易地进行测试:

代码语言:javascript
复制
    public void Test()
    {
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(30));
    }

并检查当您使用和不使用OneWay属性调用它时的行为。因此,异步调用OneWay方法有些毫无意义,尽管我怀疑这样做意味着将非常小的事情(如创建请求和发送任何数据)推送到另一个线程,因此它可能仍然有用。

AsyncPattern = true

如果您希望客户端等待操作结束(例如,在它启动另一个操作之前),这是很有用的。在OneWay的情况下,客户端将发送一个请求并忘记它-它不关心发生了什么。对于AsyncPattern,当服务完成方法的执行时,客户端将等待通知。

该模式还有一个额外的好处--如果您需要的话,它允许您在方法完成对服务的执行时运行一些代码。例如,在创建需要管理客户端处理程序并在某些事件发生时向客户端发送通知的DuplexService时,它很有用。

PS。关于你的帖子中的这一部分:“使用不是从svcutil生成的共享契约或添加服务引用”,我有点不确定。我认为这对我的回答无关紧要,但以防万一,我在这里留下了这份免责声明。;)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17322917

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档