简体   繁体   中英

Callback deadlock on wpf wfc base on windows 8/rt

whenever i try to do any actions, i receive a deadlock, but i dont know why. The method redrawTable updating every clients registered in the server is my callback. When a player does an action like drawing a card, the server responses and returns the card to the players hand. While doing this, he notifies all the other players that someone did an action. Therefore, players will automatically update everything, not just based on actions, calling the asynch methods. Thus there are another requests to the server, and i think, thats what causes the deadlock. Since the method must come from the client, there is no other workaround i guess. Searching through the web, i came to something like this:

网上的例子

I found out a lot of useful codes there, but i can't implement them in the Visual Studio 2013, since the app i want is based on Windows 8/RT. Here is the code:

My Server

//interface ICallbackContract
public interface ICallbackContract
{
    [OperationContract(IsOneWay = true)]
    void redrawTable();
}

//interface IPlayer
[ServiceContract(SessionMode = SessionMode.Required,
                 CallbackContract=typeof(ICallbackContract))]
public interface IPlayer
{
    [OperationContract(IsInitiating = true, IsTerminating = false, IsOneWay = true)]
    void login(string name);

    [OperationContract(IsInitiating = false, IsTerminating = false)]
    bool drawCard();

    [OperationContract]
    Card returnDrawnCard();

    [OperationContract]
    void thrownCard(Card throw);

    [OperationContract]
    Card returnThrownCard(Card remove);

    [OperationContract]
    Card getTopCardOfDeck();

    [OperationContract]
    Dictionary<string, CardDeck> enemyCards();

    [OperationContract]
    List<Card> getRestDeck();

    [OperationContract]
    List<Card> getThrownStack();

}

//class Player
namespace CardGameServer
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
                 ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext=true)]
[CallbackBehavior(UseSynchronizationContext = false)]
public sealed class Player : IPlayer
{
    public string Name { get; set; }
    public static List<ICallbackContract> callBackList = new List<ICallbackContract>();

    public void login(string name)
    {
        this.Name = name;

        ICallbackContract callback =  
        OperationContext.Current.GetCallbackChannel<ICallbackContract>();
        if (!callBackList.Contains(callback))
        {
            callBackList.Add(callback);
        }

        callBackList.ForEach(delegate(ICallbackContract player) { callback.redrawTable(); });
    }


    public Card returnDrawnCard()
    {
            //do something...

                foreach (ICallbackContract callback in callBackList)
                {
                    callback.redrawTable();
                }

                return true;
            }
        }
        catch (ArgumentNullException)
        {
            throw;
        }
        return false;
    }

My Client

namespace Cardgame.Client
{
public sealed partial class gamePage : Page, IPlayerCallback
{
    InstanceContext context;
    EndpointAddress address = new EndpointAddress(loginPage.serverAdresse);
    NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
    DuplexChannelFactory<IPlayer> factory;
    IPlayer myClient;

    ObservableCollection<Card> Hand = new ObservableCollection<Card>();
    ObservableCollection<Card> thrown = new ObservableCollection<Card>();
    ObservableCollection<Card> Deck = new ObservableCollection<Card>();
    ObservableCollection<Card> enemyDeck;

    public gamePage()
    {
        this.InitializeComponent();

        context = new InstanceContext(this);
        factory = new DuplexChannelFactory<ISpieler>(context, binding, address);
        myClient = factory.CreateChannel();

        login();

        throwable.ItemsSource = thrown;
        lstCards.ItemsSource = Hand;
        enemyLstCards.ItemsSource = enemyDeck;
    }

    private async void login()
    {
        await myClient.loginAsync(loginPage.username);
    }

    private async void drawCard_Tapped(object sender, TappedRoutedEventArgs e)
    {
        Hand.Add(await myClient.returnDrawnCardAsync());
    }

    public async void redrawTable()
    {
        //Update restOfDeck
                   .....
        Deck = await myClient.getRestDeckAsync();

        //Update thrownStack
                   .....
        thrown = await myClient.getThrownStackAsync();

        //Update Text

        //Update enemyHand
                   .....
        dictionary<string, CardDeck> myEnemy = await myClient.enemyCardsAsync();

    }

Linh2502, seeing your are using WCF and implementing a pub/sub model may I suggest you look into Juval Lowy's iDesign Publish-Subscribe Framework here: http://idesign.net/Downloads/GetDownload/2032

I believe you are specifically going to want to use transient subscriptions (in memory) is managed through the ISubscriptionService interface.

I believe this will help resolve your concurrency issues you are encountering.

Paul

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM