会话、实例化和并发

“会话”是在两个终结点之间发送的所有消息的一种相互关系。 “实例化”是指对用户定义的服务对象以及与其相关的 InstanceContext 对象的生存期的控制。 “并发”一词是指对 InstanceContext 中同时执行的线程数量的控制。

本主题描述这些设置、它们的使用方法以及它们之间的各种交互作用。

会话

当服务协定将 ServiceContractAttribute.SessionMode 属性设置为 SessionMode.Required时,该协定表示所有调用(即,支持调用的基础消息交换)都必须是同一个对话的一部分。 如果某个协定指定它允许使用会话但不要求使用会话,则客户端可以进行连接,并选择建立会话或不建立会话。 如果会话结束,然后在同一个基于会话的通道上发送消息,将会引发异常。

WCF 会话具有下列主要概念性功能:

  • 它们由调用应用程序显式启动和终止。

  • 会话期间传递的消息按照接收消息的顺序进行处理。

  • 会话将一组消息相互关联,从而形成对话。 该关联的含义是抽象的。 例如,一个基于会话的通道可能会根据共享网络连接来关联消息,而另一个基于会话的通道可能会根据消息正文中的共享标记来关联消息。 可以从会话派生的功能取决于关联的性质。

  • 不存在与 WCF 会话相关联的常规数据存储区。

如果你熟悉 ASP.NET 应用程序中的 System.Web.SessionState.HttpSessionState 类以及该类提供的功能,则你可能会注意到,该类型的会话和 WCF 会话之间存在以下区别:

  • ASP.NET 会话总是由服务器启动。

  • ASP.NET 会话原本是无序的。

  • ASP.NET 会话提供了一种跨请求的常规数据存储机制。

客户端应用程序和服务应用程序以不同方式与会话交互。 客户端应用程序启动会话,然后接收并处理在该会话内发送的消息。 服务应用程序可以将会话用作扩展点,以添加其他行为。 通过直接使用 InstanceContext 或实现一个自定义实例上下文提供程序,可以做到这一点。

实例化

实例化行为(使用 ServiceBehaviorAttribute.InstanceContextMode 属性进行设置)控制如何创建 InstanceContext 以响应传入的消息。 默认情况下,每个 InstanceContext 都与一个用户定义服务对象相关联,因此(在默认情况下)设置 InstanceContextMode 属性也可以控制用户定义服务对象的实例化。 InstanceContextMode 枚举定义了实例化模式。

可以使用下列实例化模式:

  • PerCall:为每个客户端请求创建一个新的 InstanceContext (以及相应的服务对象)。

  • PerSession:为每个新的客户端会话创建一个新的 InstanceContext (以及相应的服务对象),并在该会话的生存期内对其进行维护(这需要使用支持会话的绑定)。

  • Single:单个 InstanceContext (以及相应的服务对象)处理应用程序生存期内的所有客户端请求。

下面的代码示例演示 InstanceContextMode 的默认值(在服务类上显式设置了 PerSession )。

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class CalculatorService : ICalculatorInstance
{
    ...  
}  

ServiceBehaviorAttribute.InstanceContextMode 属性控制释放 InstanceContext 的频率,而 OperationBehaviorAttribute.ReleaseInstanceModeServiceBehaviorAttribute.ReleaseServiceInstanceOnTransactionComplete 属性则控制释放服务对象的时间。

已知的单一实例服务

有时,单个实例服务对象的变体是有用的:您可以自己创建一个服务对象,然后创建使用该对象的服务主机。 为此,您还必须将 ServiceBehaviorAttribute.InstanceContextMode 属性设置为 Single ,否则在打开该服务主机时将引发异常。

可使用 ServiceHost(Object, Uri[]) 构造函数创建此类服务。 当您希望提供一个特定的对象实例供单一实例服务使用时,可以使用它作为实现自定义 System.ServiceModel.Dispatcher.IInstanceContextInitializer 的替代方法。 当服务实现类型难以构造时(例如,它没有实现无参数公共构造函数),可以使用此重载。

请注意,在为此构造函数提供对象时,一些与 Windows Communication Foundation (WCF) 实例化行为相关的功能将有不同的工作方式。 例如,在提供单一实例对象实例时,调用 InstanceContext.ReleaseServiceInstance 没有任何效果。 同样,其他任何实例释放机制也都会被忽略。 ServiceHost 的行为总是像对于所有操作都将 OperationBehaviorAttribute.ReleaseInstanceMode 属性设置为 ReleaseInstanceMode.None 一样。

共享 InstanceContext 对象

通过自己执行关联,您还可以控制将哪个有会话通道或调用与哪个 InstanceContext 对象相关联。

并发

并发是对 InstanceContext 中在任一时刻处于活动状态的线程数量的控制。 此控制是通过将 ServiceBehaviorAttribute.ConcurrencyModeConcurrencyMode 枚举结合使用来实现的。

有以下三种可用的并发模式:

  • Single:最多允许每个实例上下文同时拥有一个对该实例上下文中的消息进行处理的线程。 其他希望使用同一个实例上下文的线程必须一直阻塞,直到原始线程退出该实例上下文为止。

  • Multiple:每个服务实例都可以拥有多个同时处理消息的线程。 若要使用此并发模式,服务实现必须是线程安全的。

  • Reentrant:每个服务实例一次只能处理一个消息,但可以接受可重入的操作调用。 仅当服务通过 WCF 客户端对象提供服务时才会接受这些调用。

注意

理解和开发能够安全地使用多个线程的代码可能比较困难。 在使用 MultipleReentrant 值之前,应确保已针对这些模式对服务进行了适当设计。 有关详细信息,请参阅 ConcurrencyMode

并发的使用与实例化模式有关。 在 PerCall 实例化过程中,与并发没有关系,因为每个消息都由一个新的 InstanceContext 处理,因此 InstanceContext 中处于活动状态的线程永远都不会超过一个。

下面的代码示例演示如何将 ConcurrencyMode 属性设置为 Multiple

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
public class CalculatorService : ICalculatorConcurrency
{
    ...  
}  

会话与 InstanceContext 设置进行交互

会话和 InstanceContext 根据协定中 SessionMode 枚举的值和服务实现上的 ServiceBehaviorAttribute.InstanceContextMode 属性的组合进行交互,该组合控制着通道和特定服务对象之间的联系。

下表显示了在给定服务的 ServiceContractAttribute.SessionMode 属性值和 ServiceBehaviorAttribute.InstanceContextMode 属性值组合的情况下,支持会话或不支持会话的传入通道的结果。

InstanceContextMode 值 Required Allowed NotAllowed
PerCall -会话通道的行为:每个调用都具有一个会话和 InstanceContext
- 无会话通道的行为:将引发异常。
-会话通道的行为:每个调用都具有一个会话和 InstanceContext
- 无会话通道的行为:每个调用都具有一个 InstanceContext
- 会话通道的行为:将引发异常。
- 无会话通道的行为:每个调用都具有一个 InstanceContext
PerSession - 会话通道的行为:每个通道都具有一个会话和 InstanceContext
- 无会话通道的行为:将引发异常。
- 会话通道的行为:每个通道都具有一个会话和 InstanceContext
- 无会话通道的行为:每个调用都具有一个 InstanceContext
- 会话通道的行为:将引发异常。
- 无会话通道的行为:每个调用都具有一个 InstanceContext
Single - 会话通道的行为:所有调用都具有一个会话和一个 InstanceContext
- 无会话通道的行为:将引发异常。
- 会话通道的行为:所创建的单一实例或用户指定的单一实例都具有一个会话和 InstanceContext
- 无会话通道的行为:所创建的单一实例或用户指定的单一实例都具有一个 InstanceContext
- 会话通道的行为:将引发异常。
- 无会话通道的行为:所创建的每个单一实例或用户指定的单一实例都具有一个 InstanceContext

另请参阅