none
L’application a appelé une interface qui était maintenue en ordre pour un thread différent ? RRS feed

  • Question

  • Bonjour,

    J'ai un petit soucis avec mon code, en fait j'essaye de récupérer des png sur une base de donnée en local, ce qui fonctionne plutôt pas mal, Mais lorsque j'essaye d'utiliser cette fonction pour récupérer un tilelayer, Le programme plante avec l'erreur du titre =/

    Voici mon code :

        public sealed partial class MainPage : Page
        {
            private SQLiteConnection conn;
            private BitmapImage Img;
            public MainPage()
            {
                this.InitializeComponent();
                conn = new SQLiteConnection("map.mbtiles");
                Img = new BitmapImage(); // Probleme
                conn.BeginTransaction();
                MapTileLayer layer = new MapTileLayer();
                layer.Opacity = 1;
                layer.GetTileUri += (s, e) =>
                {
                    e.Uri = TestAsync(e.LevelOfDetail, e.X, e.Y).Result;
                  };
                MyMap.TileLayers.Add(layer);
            }
    
            public async Task<Uri> TestAsync(int Z, int X, int Y)
            {
                var cmd = new SQLiteCommand(conn);
                cmd.CommandText = string.Format("SELECT [tile_data] FROM [tiles] WHERE zoom_level = {0} AND tile_column = {1} AND tile_row = {2}"
                    , Z, X, Y);
                Byte[] TileObj = cmd.ExecuteScalar<Byte[]>();
                if (TileObj != null)
                {
                    var ms = new MemoryStream(TileObj);
                    await Img.SetSourceAsync(ms.AsRandomAccessStream());
                    System.Diagnostics.Debug.WriteLine(Img.UriSource);
                    return Img.UriSource;
                }
                return null;
            }
    }

    L'idée c'est de détourné l'eventhandler gettileuri du sdk bing map pour y caller non pas le uri d'un serveur mais l'uri de l'image sortie de la db.

    D'avance merci =)

    mardi 8 septembre 2015 15:54

Réponses

  • Je ne connais pas bien (voir pas du tout) la gestion des cartes, mais si "layer.GetTileUri" a lieu dans un autre thread, il faut synchroniser avec le dispatcher je pense.

    Toutefois en regardant de plus prêt je ne vois pas comment votre code peut fonctionner, vous chargez vos images depuis une base de données, ensuite vous demander l'URI source, normalement elle n'existe pas dans ce cas là. Vous avez quelque chose qui s'affiche dans votre fenêtre de déboggage ?

    Concernant Task, Task<T> Fct() renvoi une tâche (Task<T>). Cette tâche peut être manipulée avec les méthodes classiques de la TPL (Wait(), ContinueWith(), .Result, etc.) ou avec les nouveaux mots clé "await/async". "await/async" s'applique à tout objet qui peut être "attendu" dont font partie les tâches. "await/async" fait une sorte de "Wait() et/ou .Result" avec une synchronisation entre les threads (ce qui facilite le travail du développeur). Toutefois le "await/async" n'est pas franchement compatible avec les méthodes de la TPL, donc soit on utilise l'un soit on utilise l'autre, mais jamais les deux simultanément (risque d'interblocage en particulier).

    Olivier Dahan a écrit des articles à ce sujet : http://www.e-naxos.com/Blog/post/De-la-bonne-utilisation-de-AsyncAwait-en-C.aspx

    Ainsi que toute une série d'articles détaillés (12 de mémoire) sur les tâches : http://www.e-naxos.com/Blog/post/Task-qui-es-tu-partie-1.aspx

    http://www.e-naxos.com/Blog/?tag=/asynchronisme

    Il y a pas mal de lecture mais c'est intéressant et utile à savoir.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.


    mercredi 9 septembre 2015 13:06

Toutes les réponses

  • Bonjour,

    Parce que vous exécutez votre code dans un autre thread.

    Il faut appeler votre méthode avec await ou alors le resynchroniser avec le SynchronizationContext. (Exemple ici)

    Mais ce n'est pas bien dans le constructeur d'appeler une méthode asynchrone ;-)

    Il est préférable de le faire dans votre event Loaded de la page et dans ce cas, vous pouvez utiliser await/async normalement :

    public async void MainPage_Loaded(){...
    e.Uri = await TestAsync(e.LevelOfDetail, e.X, e.Y);


    Richard Clark
    Consultant - Formateur .NET
    http://www.c2i.fr
    Depuis 1996: le 1er site .NET francophone

    mercredi 9 septembre 2015 05:29
  • Bonjour,

    Je ne vois pas en quoi ca pose un problème car ce n'est pas dans le constructeur mais dans un événement (ou alors j'ai loupé quelque chose).

    Perso surtout ce que je vois qui n'est pas bien c'est qu'il y a un mélange entre la synchronisation Task (utilisation du ".Result") et await/async. Ce qui est incompatible. Donc il faut tout appliquer dans un mode ou dans un autre mais pas les deux :

    layer.GetTileUri += async (s, e) =>
                {
                    e.Uri = await TestAsync(e.LevelOfDetail, e.X, e.Y);
                  };
    
    Cordialement


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    mercredi 9 septembre 2015 06:25
  • Bonjour,

    Tu as lu un peu vite : cette ligne, e.Uri =... est placée  dans le constructeur de la MainPage.

    Donc on ne peut mettre async devant.


    Richard Clark
    Consultant - Formateur .NET
    http://www.c2i.fr
    Depuis 1996: le 1er site .NET francophone

    mercredi 9 septembre 2015 06:28
  • Je ne saisi pas :) Pour moi le "e.Uri = .." est dans l'événement layer.GetTileUri, qui lui est défini dans le constructeur. C'est l'événement qui est async pas le constructeur dans notre cas.

    Définir un événement async depuis le constructeur d'une page je le fais (j'ai vérifié avant de poster :)) dans un de mes projets en cours.

    A moins qu'il y ai quelque chose qui m'échappe, c'est possible je n'ai pas encore pris mon café ;)

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    mercredi 9 septembre 2015 06:34
  • Hips ;-)

    Faut que j'arrête le cognac au p'tit déj. C'est moi qui ai mal lu.


    Richard Clark
    Consultant - Formateur .NET
    http://www.c2i.fr
    Depuis 1996: le 1er site .NET francophone

    mercredi 9 septembre 2015 06:54
  • En effet c'est peut-être un peu tôt ;)


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    mercredi 9 septembre 2015 06:57
  • Merci pour vos réponses =)

    Bon du coup j'ai crée un event sur le chargement de la page, mais je rencontre toujours le même type d'erreurs =/

    Pourtant ce coup ci l'event GetTileUri est bien exécuté en Asynchrone =/

    Voici le nouveau code :

    J'ai toujours une erreur sur la ligne :

    e.Uri = await TestAsync(e.LevelOfDetail, e.X, e.Y);

    Qui est :

        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            public async Task<Uri> TestAsync(int Z, int X, int Y)
            {
                SQLiteConnection conn = new SQLiteConnection("map.mbtiles");
                BitmapImage Img = new BitmapImage();
                var cmd = new SQLiteCommand(conn);
                cmd.CommandText = string.Format("SELECT [tile_data] FROM [tiles] WHERE zoom_level = {0} AND tile_column = {1} AND tile_row = {2}"
                    , Z, X, Y);
                Byte[] TileObj = cmd.ExecuteScalar<Byte[]>();
                if (TileObj != null)
                {
                    var ms = new MemoryStream(TileObj);
                    await Img.SetSourceAsync(ms.AsRandomAccessStream());
                    System.Diagnostics.Debug.WriteLine(Img.UriSource);
                    return Img.UriSource;
                }
                return null;
            }
    
            public void Page_Loaded(object sender, RoutedEventArgs a)
            {
                MapTileLayer layer = new MapTileLayer();
                layer.Opacity = 1;
                layer.GetTileUri += async (s, e) =>
                {
                    e.Uri = await TestAsync(e.LevelOfDetail, e.X, e.Y);
                };
                MyMap.TileLayers.Add(layer);
            }
        }

    Exception:Levée : "L’application a appelé une interface qui était maintenue en ordre pour un thread différent.
    "

    Autre question en passant =) :

    En fait lorsque qu'on lance un async task<T> Fct() , Fct() correspond à la tache en elle même et fct().Result à son résultat jusqu'à maintenant , et un await fct(); correspond au résultat final ?

    C'est juste pour être certain du comportement des task =)

    D'avance merci =)



    mercredi 9 septembre 2015 08:41
  • Je ne connais pas bien (voir pas du tout) la gestion des cartes, mais si "layer.GetTileUri" a lieu dans un autre thread, il faut synchroniser avec le dispatcher je pense.

    Toutefois en regardant de plus prêt je ne vois pas comment votre code peut fonctionner, vous chargez vos images depuis une base de données, ensuite vous demander l'URI source, normalement elle n'existe pas dans ce cas là. Vous avez quelque chose qui s'affiche dans votre fenêtre de déboggage ?

    Concernant Task, Task<T> Fct() renvoi une tâche (Task<T>). Cette tâche peut être manipulée avec les méthodes classiques de la TPL (Wait(), ContinueWith(), .Result, etc.) ou avec les nouveaux mots clé "await/async". "await/async" s'applique à tout objet qui peut être "attendu" dont font partie les tâches. "await/async" fait une sorte de "Wait() et/ou .Result" avec une synchronisation entre les threads (ce qui facilite le travail du développeur). Toutefois le "await/async" n'est pas franchement compatible avec les méthodes de la TPL, donc soit on utilise l'un soit on utilise l'autre, mais jamais les deux simultanément (risque d'interblocage en particulier).

    Olivier Dahan a écrit des articles à ce sujet : http://www.e-naxos.com/Blog/post/De-la-bonne-utilisation-de-AsyncAwait-en-C.aspx

    Ainsi que toute une série d'articles détaillés (12 de mémoire) sur les tâches : http://www.e-naxos.com/Blog/post/Task-qui-es-tu-partie-1.aspx

    http://www.e-naxos.com/Blog/?tag=/asynchronisme

    Il y a pas mal de lecture mais c'est intéressant et utile à savoir.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.


    mercredi 9 septembre 2015 13:06