Meilleur auteur de réponses
L’application a appelé une interface qui était maintenue en ordre pour un thread différent ?

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 =)
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.- Modifié Yan Grenier - MTFC mercredi 9 septembre 2015 13:07 Ajout d'un lien
- Marqué comme réponse ThatGuyWithTheGlasses lundi 14 septembre 2015 08:10
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 -
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.- Proposé comme réponse Richard ClarkMVP mercredi 9 septembre 2015 06:54
-
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 -
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. -
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 -
-
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 =)
- Modifié ThatGuyWithTheGlasses mercredi 9 septembre 2015 08:47
-
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.- Modifié Yan Grenier - MTFC mercredi 9 septembre 2015 13:07 Ajout d'un lien
- Marqué comme réponse ThatGuyWithTheGlasses lundi 14 septembre 2015 08:10