|
这一部分我将简单的介绍我门的具体代码实现,因为代码中已经做了许多解释,所以不再浪费口舌,我简单的粘贴如下,我想阐述以下关于线程并发的一些问题.
服务契约的定义:
namespace JeffBarnes.WCF.Samples.CallbackDemo.Service
  {
// NOTE: The use of one way service operations allows the callback to occur while
// still using the single concurrency mode rather than Reentrant or Multiple.

 /**//// <summary>
/// Beer Inventory Service Operations
/// </summary>
[ServiceContract(
Name = BeerInventoryService,
Namespace = http://jeffbarnes.net/wcf/samples/client_callback/BeerInventoryService/,
SessionMode = SessionMode.Required,
CallbackContract = typeof(IBeerInventoryCallback))]
public interface IBeerInventory
 {
 /**//// <summary>
/// Guest arrived at the party for the sole purpose of drinking beer
/// </summary>
/// <param name=name>Name of the guest</param>
/// <return>Number of available beers</return>
[OperationContract()]
int JoinTheParty(string guestName);

 /**//// <summary>
/// Someone graciously brought beer to the party
/// </summary>
/// <param name=numberOfBeers>Number of beers brought</param>
[OperationContract(IsOneWay = true)]
void MakeBeerRun(string guestName, int numberOfBeers);

 /**//// <summary>
/// Someone drank a beer
/// </summary>
[OperationContract(IsOneWay = true)]
void DrinkBeer(string guestName);

 /**//// <summary>
/// You dont have to go home, but you cant stay here
/// </summary>
[OperationContract(IsOneWay = true)]
void LeaveTheParty(string guestName);
}
}

回调契约的定义:
namespace JeffBarnes.WCF.Samples.CallbackDemo.Service
  {
// NOTE: The use of one way service operations allows the callback to occur while
// still using the single concurrency mode rather than Reentrant or Multiple.

 /**//// <summary>
/// Beer Inventory Callback Service Operations
/// </summary>
/// <remarks>
/// The ServiceContract attribute is not explicitly required for the callback interface.
///
/// By specifying IBeerInventoryCallback as the CallbackContract in IBeerInventory,
/// an implicit ServiceContract attribute is applied to this interface.
/// </remarks>
public interface IBeerInventoryCallback
 {
 /**//// <summary>
/// Notifies the client that a guest has joined the party
/// </summary>
/// <param name=guestName>Name of the guest</param>
[OperationContract(IsOneWay = true)]
void NotifyGuestJoinedParty(string guestName);

 /**//// <summary>
/// Notifies the client that beer inventory has changed
/// </summary>
/// <param name=guestName>Name of the guest</param>
/// <param name=numberOfBeers>Number of beers that were drank or brought</param>
[OperationContract(IsOneWay = true)]
void NotifyBeerInventoryChanged(string guestName, int numberOfBeers);

 /**//// <summary>
/// Notifies the client that a guest has left the party
/// </summary>
/// <param name=guestName>Name of the guest</param>
[OperationContract(IsOneWay = true)]
void NotifyGuestLeftParty(string guestName);
}
}
我门为了更好的呈现我门的服务,我门或许设计一个客户端的窗体。譬如我门也许设计如下的窗体。然后,我门希望,通过我门的窗体界面来调用我门的服务。
现在,当用户操作窗体的时候,相应的操作调用远程服务,对订阅者来说,一些操作将导致回调操作。然而,在windows窗体程序和WPF程序下进行具有回调操作的远程服务的调用将会引起一个问题。为什么我以前没碰到过这中情况呢,那是因为或许你的客户端程序是Console程序。如果窗体线程调用一个服务操作(具有回调操作的服务),将会引起死琐。
假设我门的编码有这么一句话。 this.BeerInventory=_proxy.JoinTheParty(this.txtGuestName.Text); this是窗体,BeerInventory是窗体属性。乍一看,好象没有问题。但是考虑一下什么将会发生。当服务操作从窗体线程被调用,它将锁住直到返回值返回。然而,在发送回返回值以前,服务操作会调用一个发生在客户端的回调操作,而回调操作是被Marshal在窗体线程下。因为窗体线程为了返回值仍然在苦苦等待,死锁将会发生,其实问题的根源与同步上下文有关。同步上下文也是一中上下文,这我上文已经进行了阐述。我门说过回调操作,让服务端线程和客户端线程确立了联姻关系。客户端的回调操作将在一个特殊的客户端线程中执行。我门在设计客户端的时候,通常要设计友好的窗体界面,然后操作窗体控件,请注意,操作系统为了保证这些窗体或者对窗体控件的操作不被任何外界破坏,当然需要一个相关的线程来提供这中保障。但是,如果我门把回调操作放在这个窗体线程中,就回发生一个有意思的问题,与服务端建立联姻关系的回调线程和窗体线程是同一个线程。尽管在一个线程下回调操作可以操作窗体属性,但是回引发死锁。
让客户端的回调操作脱离同步上下文是一中简单尝试。幸运的是,WCF提供了一个简单的机制来让客户端的操作脱离同步上下文,通过覆盖同步上下文的自动的联姻。同步上下文的这中行为能够被关闭,通过设置CallbackBehavior的属性UseSynchronizationContext为false.如果做此,WCF将不在保证提供一个特别的线程来保证客户端的回调操作与服务端具有联姻关系。相反,回调操作将被执行在一个工作线程中。 [CallbackBehavior(UseSynchronizationContext = false)]
这样的话,执行回调操作的线程就与窗体操作的线程不是一个线程。但是又引起一个新的问题。既然不是一个线程,那么回调操作怎么能操作窗体的属性或者方法或者控件呢?确实不能。
在回调操作的线程中我门可以执行一个特别的委托,而这中委托恰恰提供了这么一种机制,通过它的纽带作用使的具有回调操作的线程能够与窗体线程通讯的能力。 SendOrPostCallback callback = delegate (object state) { this.WritePartyLogMessage(String.Format({0} has joined the party., state.ToString())); };
_uiSyncContext.Post(callback, guestName); 至此。我门的问题已经全部解决。 我这里只是做个大概介绍,更多解释和代码实现请阅读源代码。
WCF:双向通讯,枷锁机制,线程并发的一些简单理解(二) |