Exporter (0) Imprimer
Développer tout
Ce sujet n'a pas encore été évalué - Évaluez ce sujet

Comportement en temps réel de .NET Compact Framework

Maartin Struys
Michel Verhagen
PTS Software

S'applique à :
    Microsoft® Windows® CE .NET
    Microsoft Visual Studio® .NET
    Microsoft Visual Basic® .NET
    Microsoft Visual C#®
    Microsoft .NET Compact Framework

Téléchargez CFInRT_used_for_actual_measurements.exe.

Téléchargez RTCF.exe.

Téléchargez Win32CEAppInRT.exe.

Résumé : Avec la sortie de Visual Studio .NET 2003, qui intègre la prise en charge de Smart Device Programmability, il est possible de développer des applications pour une vaste gamme de périphériques à partir de code géré. Les développeurs de logiciels peuvent désormais utiliser des langages captivants comme Visual Basic .NET ou Visual C# pour le développement de périphériques. Si cela semble prometteur, une question demeure : est-il possible d'utiliser les fonctionnalités temps réel de Windows CE .NET et d'écrire en code géré des applications destinées à un appareil embarqué ? Cet article répond à cette question et propose un scénario permettant d'associer le comportement en temps réel et les fonctionnalités propres à Microsoft .NET.

Sommaire

Environnement géré et environnement non géré
La fonction Platform Invoke
Un scénario en temps réel
Le test en lui-même
Les résultats
Les pièges
La vérification des résultats
Conclusion
Remerciements

Environnement géré et environnement non géré

Certains avantages d'un environnement géré tel que Microsoft® Common Language Runtime, comme la sécurité d'écriture ou le fait que le logiciel soit indépendant de la plate-forme, deviennent des inconvénients dans un environnement en temps réel. En général, vous ne pouvez pas vous permettre d'attendre que le compilateur juste-à-temps (JIT) compile une méthode avant de l'utiliser, ou encore que le Garbage Collector efface la mémoire précédemment allouée en éliminant les ressources non utilisées. Ces deux fonctionnalités peuvent interférer avec un comportement système déterministe. Il est possible d'obliger le Garbage Collector à effectuer son travail en appelant GC.Collect(). Cependant, du fait que le Garbage Collector est fortement optimisé, il est préférable de le laisser travailler de façon autonome. Pour permettre un comportement en temps réel « dur », il serait utile de disposer d'un moyen de distinguer les fonctionnalités en temps réel dur, écrites en code natif ou en code Microsoft Win32® non géré, des autres fonctionnalités écrites en code géré. C'est ce que vous permet de faire Platform Invoke ou P/Invoke.

La fonction Platform Invoke

Selon l'aide MSDN®, P/Invoke est la fonctionnalité fournie par le Common Language Runtime qui permet à du code géré d'appeler les points d'entrée d'une bibliothèque de liaison dynamique (DLL) native non gérée. En d'autres termes, P/Invoke vous permet d'échapper au code géré de Microsoft .NET et d'opter pour du code non géré Win32. Pour que vous puissiez utiliser ce mécanisme dans Microsoft Windows® CE .NET, les fonctions Win32 natives que vous souhaitez appeler doivent être définies comme des fonctions publiques externes au sein d'une DLL. Comme l'environnement .NET géré ignore tout de la décomposition des noms (name mangling) C++, les fonctions à appeler à partir d'une application gérée doivent aussi respecter les conventions de désignation C. Pour pouvoir utiliser la fonctionnalité depuis une DLL, il vous faut créer une classe wrapper autour des points d'entrée de la fonction à partir de votre application gérée. L'extrait 1 illustre un exemple de petite DLL non gérée. L'extrait 2 explique comment appeler cette DLL à partir de code géré. Comme ce mécanisme fonctionne pour toutes les fonctions DLL exportées et que presque toutes les API Win32 sont exportées dans coredll.dll, ce mécanisme permet aussi d'effectuer des appels à presque toutes les API Win32. Dans notre test, nous avons utilisé P/Invoke pour que l'application gérée lance un appel dans une thread en temps réel non gérée.

// This is the function GetTimingInfo that exists in the
// unmanaged Win32 DLL. The function is fed with information,
// originating in an Interrupt Service Thread in the same 
// DLL. On request of the managed application, timing 
// information is copied using a double buffering mechanism.
RTCF_API DWORD GetTimingInfo(LPDWORD lpdwAvgPerfTicks,
 LPDWORD lpdwMax,
 LPDWORD lpdwMin,
 LPDWORD lpdwDeltaMax,
 LPDWORD lpdwDeltaMin)
{
 g_bRequestData = TRUE;
 if (WaitForSingleObject(g_hNewDataEvent,
 1000)==WAIT_OBJECT_0)
 {
 *lpdwAvgPerfTicks = g_dwBufferedAvgPerfTicks;
 *lpdwMax = g_dwBufferedMax;
 *lpdwMin = g_dwBufferedMin;
 *lpdwDeltaMax = g_dwBufferedDeltaMax;
 *lpdwDeltaMin = g_dwBufferedDeltaMin;
 return 1;
 }
 else
 return 0;
}

// GetTimingInfo prototype
#ifdef RTCF_EXPORTS
#define RTCF_API __declspec(dllexport)
#else
#define RTCF_API __declspec(dllimport)
#endif

extern "C"
{
 RTCF_API BOOL Init();
 RTCF_API BOOL DeInit();
 RTCF_API DWORD GetTimingInfo(LPDWORD lpdwAvgPerfTicks,
 LPDWORD lpdwMax,
 LPDWORD lpdwMin,
 LPDWORD lpdwDeltaMax,
 LPDWORD lpdwDeltaMin);
}

Extrait 1. DLL Win32 appelée à partir de code géré

// Wrapper class to be able to P/Invoke into a DLL.
// Exported functions in the DLL are imported by this
// wrapper. Note the use of compiler attributes to identify
// the physical DLL that hosts the exported functions.
using System;
using System.Runtime.InteropServices;

namespace CFinRT
{
 public class WCEThreadIntf
 {
 [DllImport("RTCF.dll")]
 public static extern bool Init();
 [DllImport("RTCF.dll")]
 public static extern bool DeInit();
 [DllImport("RTCF.Dll")]
 public static extern uint GetTimingInfo(
 ref uint perfAvg,
 ref uint perfMax,
 ref uint perfMin,
 ref uint perfTickMax,
 ref uint perfTickMin);
 }
}

// Call an unmanaged function from within managed code 
public void CollectValue() 
{
 if (WCEThreadIntf.GetTimingInfo(ref aveSleepTime,
 ref maxSleepTime,
 ref minSleepTime,
 ref curMaxSleepTime,
 ref curMinSleepTime) != 0) 
 {
 curMaxSleepTime = (uint)(float)((curMaxSleepTime *
 scaleValue) / 1.19318);
 curMinSleepTime = (uint)(float)((curMinSleepTime *
 scaleValue) / 1.19318);
 aveSleepTime = (uint)(float)((aveSleepTime *
 scaleValue) / 1.19318);
 maxSleepTime = (uint)(float)((maxSleepTime *
 scaleValue) / 1.19318);
 minSleepTime = (uint)(float)((minSleepTime *
 scaleValue) / 1.19318);
 } 

 StoreValue();
 counter = (counter + 1) % samplesInMinute;
}

Extrait 2. Appel de code non géré

Un scénario en temps réel

Un système doit disposer de fonctionnalités en temps réel dur pour pouvoir récupérer des informations provenant d'une source externe. Ces informations sont stockées dans le système et présentées à l'utilisateur sous une forme graphique. La figure 1 illustre un scénario possible permettant de gérer ce problème.

Scénario en temps réel utilisant à la fois du code géré et du code non géré

Figure 1. Scénario en temps réel utilisant à la fois du code géré et du code non géré

Une thread en temps réel résidant dans une DLL Win32 native reçoit une interruption d'une source externe. La thread traite l'interruption et stocke les informations pertinentes à présenter à l'utilisateur. Du côté droit, une thread d'interface utilisateur séparée, écrite en code géré, lit les informations précédemment stockées par la thread en temps réel. Les changements de contexte entre processus étant coûteux, mieux vaut que tout le système réside dans le même processus. Si vous séparez la fonctionnalité temps réel de la fonctionnalité d'interface utilisateur en plaçant la première dans une DLL et en procurant une interface entre cette DLL et les autres parties du système, vous atteindrez votre objectif : un seul processus qui gère toutes les parties du système. La communication entre la thread de l'interface utilisateur et celle en temps réel s'effectue en accédant au code Win32 natif via P/Invoke.

Le test en lui-même

Vous souhaitez que votre test soit représentatif mais qu'il reste aussi simple que possible pour qu'il puisse être reproduit facilement sur d'autres systèmes. Vous pouvez pour cela télécharger le code source qui vous permettra d'exécuter ce test vous-même. Pour réaliser ce test, il faut pouvoir introduire des interruptions dans le système et effectuer des sondages pour mesurer les performances du système. Vous alimentez le système en utilisant un signal de blocage, produit par un générateur de signal. Bien sûr, le système d'exploitation de Windows CE .NET doit être capable d'héberger .NET Compact Framework. Dans l'un de ses articles, Paul Yao indique les modules et composants Windows CE .NET qui doivent être présents pour permettre d'exécuter des applications gérées. Voir Microsoft .NET Compact Framework for Windows CE .NET ( Lien externe au site MSDN France Site en anglais). Le test n'a pas pour seul objectif d'être représentatif et reproductible, ni de trouver une source d'interruption adaptée. L'extrait 3 montre comment connecter une interruption physique à une thread de service d'interruption.

RTCF_API BOOL Init()
{
 BOOL bRet = FALSE;
 DWORD dwIRQ = IRQ; // in our case IRQ = 5

 // Get a SysIntr for the specified IRQ 
 if (KernelIoControl(IOCTL_HAL_TRANSLATE_IRQ,
 &dwIRQ,
 sizeof(DWORD),
 &g_dwSysIntr,
 sizeof(DWORD),
 NULL))
 {
 // create an event that will activate our IST
 g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

 if (g_hEvent)
 {
 // Connect the interrupt to our event and
 // create our Interrupt Service Thread.
 // The actual IST is shown in listing 4
 InterruptDisable(g_dwSysIntr);

 if (InterruptInitialize(g_dwSysIntr,
 g_hEvent, NULL, 0))
 {
 g_bFinish = FALSE;
 g_hThread = CreateThread(NULL,
 0,
 IST,
 NULL,
 0,
 NULL);
 if (g_hThread)
 {
 bRet = TRUE;
 }
 else
 {
 InterruptDisable(g_dwSysIntr);
 CloseHandle(g_hEvent);
 g_hEvent = NULL;
 }
 }
 }
 }
 return bRet;
}

Extrait 3. Comment connecter une interruption physique à une thread de service d'interruption.

Pour tester le comportement en temps réel d'une application utilisant du code géré et .NET Compact Framework, nous avons créé une plate-forme Windows CE .NET basée sur le kit SDK Standard. Nous avons aussi inclus dans la plate-forme la version RTM de .NET Compact Framework. Le système d'exploitation utilise un Geode GX1 fonctionnant à 300 MHz. Nous alimentons le système avec un signal de blocage, immédiatement connecté à la ligne IRQ5 du bus PC104 (broche 23). La fréquence du signal de blocage est de 10 kHz. Sur les flancs montants, une interruption est générée. L'interruption est traitée par une thread de service d'interruption (IST, Interrupt Service Thread). Dans l'IST, nous envoyons des impulsions de sondage au port parallèle de façon à visualiser un signal de sortie. Nous enregistrons également l'heure à laquelle l'IST a été activée grâce à l'API haute résolution QueryPerformanceCounter. Pour pouvoir mesurer les informations de minutage sur une longue période, nous enregistrons, outre la durée moyenne, la durée maximum et la durée minimum. Le laps de temps qui s'écoule entre l'occurrence de l'interruption et la vérification en sortie indique la latence IRQ - IST. Les informations de minutage collectées par l'horloge haute résolution indiquent quand l'IST est activée. Dans l'idéal, cette valeur doit être de 100 µs pour une fréquence d'interruption de 10 kHz. Toutes les informations de minutage sont transmises à l'interface utilisateur graphique à intervalles réguliers.

Comme .NET Compact Framework ne peut pas être utilisé dans des situations en temps réel dur comme celles décrites précédemment, nous avons décidé de ne l'utiliser que pour la présentation et de recourir à une DLL écrite avec Microsoft Visual C++® 4.0 incorporé pour toutes les fonctionnalités en temps réel. Pour la communication entre la DLL et l'interface utilisateur graphique (GUI, Graphical User Interface) .NET Compact Framework, nous utilisons un mécanisme de double tampon associé à P/Invoke. L'interface graphique, qui nécessite de nouvelles informations de minutage à intervalles réguliers, utilise un objet System.Threading.Timer. La DLL décide, en fonction de sa disponibilité, du moment opportun pour transmettre les informations à l'interface graphique. Cette dernière reste bloquée jusqu'à ce que les données soient prêtes. L'utilisateur peut sélectionner la fréquence d'actualisation des informations présentées dans l'interface graphique. Dans notre test, nous avons opté pour une fréquence d'actualisation de 50 ms.

Le pseudocode suivant explique le fonctionnement de l'IST et le mécanisme utilisé par l'interface graphique pour récupérer les informations stockées dans la DLL Win32 native.

Interrupt Service Thread:
Wait
On IRQ 5 send probe pulse to the parallel port
Measure time with QueryPerformanceCounter
Store measured time (min, max, current, average) locally
if (userInterfaceRequestsData) {
 copy measured time information
 reset statistic measure values
 set dataReady event
 userInterfaceRequestsData = false
}

Mise à jour périodique en code géré des données d'affichage :

disable timer // See pitfalls
call with P/Invoke into the DLL
// The following code is implemented in the DLL
userInterfaceRequestsData = true
wait for dataReady event
return measured values
draw measured values on the display, each time using new graphics objects
update marker // A running vertical bar on the display
enable timer

Lors du test, nous avons connecté un oscilloscope et imprimé au bout de dix minutes de test une capture de l'affichage graphique de l'oscilloscope et de Windows CE .NET. La figure 2 montre la latence d'interruption mesurée par l'oscilloscope. Dans le meilleur des cas, la latence est de 14 µs, dans le pire de cas, elle est de 54,4 µs, ce qui représente une instabilité de 40,4 µs. La figure 3 montre les données périodiques d'activation de l'IST. Cette figure est une capture d'écran de l'interface utilisateur réelle. Dans l'idéal, l'IST doit s'exécuter toutes les 100 µs, ce qui correspond au temps moyen utilisé lors de nos mesures (ligne bleue). Nous avons aussi mesuré les temps globaux minimum (en vert) et maximum (en rouge), en plus des temps minimum et maximum pour la période d'échantillonnage de 50 millisecondes (pavé représenté en blanc). L'écart obtenu pendant la période de test est limité à ±40 µs.

Application gérée : latence IRQ. IST

Figure 2. Application gérée : latence IRQ. IST

Application gérée : temps d'activation de l'IST après une durée d'exécution de 10 minutes

Figure 3. Application gérée : temps d'activation de l'IST après une durée d'exécution de 10 minutes

Les résultats

Nous avons effectué les mesures sur une plus longue période pour garantir que le Garbage Collector et le compilateur JIT seraient fréquemment actifs. Grâce à nos collègues de Microsoft, qui nous ont fourni une clé de registre pour les compteurs de performance, nous avons pu suivre le comportement de .NET Compact Framework. Cette clé permet d'activer plusieurs compteurs de performance de .NET Compact Framework. Nous avons utilisé ces informations de performance surtout pour vérifier le bon fonctionnement du compilateur JIT et du Garbage Collector. Elles nous ont également fourni des indications utiles sur le nombre d'objets utilisés pendant le test.

// Our periodic timer method in which we want to collect new
// data and refresh the screen
private void OnTimer(object source) 
{
 // Temporarily stop the timer, to prevent against
 // a whole bunch of OnTimer calls to be invoked
 if (theTimer != null) 
 {
 theTimer.Change(Timeout.Infinite, dp.Interval);
 }
 Pen blackPen = new Pen(Color.Black);
 Pen yellowPen = new Pen(Color.Yellow);
 Graphics gfx = CreateGraphics();

 td.SetTimePointer(dp.CurrentSample, gfx, blackPen);

 for (int i = 0; i < dp.SamplesPerMeasure; i++) 
 {
 td.ShowValue(dp.CurrentSample, dp[i], gfx, i);
 }

 dp.CollectValue();
 td.SetTimePointer(dp.CurrentSample, gfx, yellowPen);

 gfx.Dispose();
 yellowPen.Dispose();
 blackPen.Dispose();

 // Restart the timer again for the next update
 if (theTimer != null) 
 {
 theTimer.Change(dp.Interval, dp.Interval);
 }
}

Extrait 4. Gestion des messages de l'horloge dans un environnement géré

Comme le montre l'extrait 4, nous instancions plusieurs objets à chaque mise à jour périodique de l'écran. Ces objets (deux stylos et un objet graphique) sont créés chaque fois que l'écran est mis à jour. Les fonctions td.ShowValue et td.SetTimerPointer créent également des pinceaux. Comme la fonction td.SetTimerPointer est appelée deux fois à chaque mise à jour de l'écran, six objets sont créés au total chaque fois que l'écran est mis à jour. Étant donné que l'écran est mis à jour toutes les 50 ms, 120 objets sont créés chaque seconde. Après 10 minutes d'exécution, le nombre d'objets créés s'élève à 72 000. Tous ces objets peuvent être soumis à un nettoyage de la mémoire (garbage collection). Dans le tableau 1, le nombre d'objets alloués correspond approximativement à ces valeurs théoriques.

CompteurValeurnmoyenneminmax
Temps d'exécution total du programme6037520000
Nombre d'octets maximum alloués11152380000
Nombre d'objets alloués668980000
Nombre d'octets alloués14182166689821824020
Nombre de nettoyages simples00000
Nombre d'octets libérés à chaque nettoyage simple00000
Nombre d'octets en cours d'utilisation après un nettoyage simple00000
Temps passé en nettoyage simple00000
Nombre de nettoyages compacts10000
Nombre d'octets libérés à chaque nettoyage compact6524201652420652420652420
Nombre d'octets en cours d'utilisation après un nettoyage compact1340201134020134020134020
Temps passé en nettoyage compact3571357357357
Nombre de nettoyages complets00000
Nombre d'octets libérés à chaque nettoyage complet00000
Nombre d'octets en cours d'utilisation après un nettoyage complet00000
Temps passé en nettoyage complet00000
Nombre d'opérations de nettoyage GC ( garbage collection) générées par l'application00000
Temps de latence GC3571357357357
Nombre d'octets compilés avec Jit14046259541929
Nombre d'octets natifs compilés avec Jit70636259272353758
Nombre de méthodes compilées avec Jit2590000
Nombre d'octets éliminés00000
Nombre de méthodes éliminées00000
Nombre d'exceptions00000
Nombre d'appels30586070000
Nombre d'appels virtuels14090000
Nombre de demandes satisfaites par le cache d'appels virtuels13760000
Nombre d'appels de PInvoke1767900000
Nombre total d'octets en cours d'utilisation après un nettoyage4214621421462421462421462

Tableau 1. Résultats des performances de .NET Compact Framework pour une durée d'exécution du test de cinq minutes

Nous avons inclus les résultats des compteurs de performance obtenus pour une durée d'exécution de 10 minutes et pour une durée d'exécution de 100 minutes. Ces données ont été enregistrées durant le test réel. Comme vous pouvez le constater, après 10 minutes d'exécution, l'opération de nettoyage de la mémoire s'est produite sans baisse significative des performances. Le tableau 2 montre les compteurs de performance pour une durée d'exécution d'environ 100 minutes. Cette durée d'exécution a permis la réalisation d'un nettoyage de la mémoire complet. Sur cette durée, seuls 461 499 objets ont été créés au lieu des 720 000 escomptés. Cela représente environ 35 % d'objets en moins que prévu. Cette différence est probablement due aux compteurs de performance qui, selon Microsoft, génèrent une altération des performances d'environ 30 % dans l'application gérée. Quoi qu'il en soit, comme le montre la figure 4, le comportement en temps réel du système n'a pas été affecté.

CompteurValeurnmoyenneminmax
Démarrage du moteur d'exécution4780000
Durée totale d'exécution du programme58449460000
Nombre d'octets maximum alloués12796780000
Nombre d'objets alloués4614990000
Nombre d'octets alloués897558446149919824020
Nombre de nettoyages simples00000
Nombre d'octets libérés à chaque nettoyage simple00000
Nombre d'octets en cours d'utilisation après un nettoyage simple00000
Temps passé en nettoyage simple00000
Nombre de nettoyages compacts110000
Nombre d'octets libérés à chaque nettoyage compact851491211774082656456786476
Nombre d'octets en cours d'utilisation après un nettoyage compact167965611152696147320153256
Temps passé en nettoyage compact53950490436542
Nombre de nettoyages complets20000
Nombre d'octets libérés à chaque nettoyage complet39742821987141916395512
Nombre d'octets en cours d'utilisation après un nettoyage complet799242399621732862596
Temps passé en nettoyage complet65232263
Nombre d'opérations de nettoyage GC (garbage collection) générées par l'application00000
Temps de latence GC5460134202542
Nombre d'octets compilés avec Jit19143356531929
Nombre d'octets natifs compilés avec Jit95684356268353758
Nombre de méthodes compilées avec Jit3560000
Nombre d'octets éliminés85304326261353758
Nombre de méthodes éliminées3850000
Nombre d'exceptions00000
Nombre d'appels217781240000
Nombre d'appels virtuels10670000
Nombre de demandes satisfaites par le cache d'appels virtuels10290000
Nombre d'appels de PInvoke19969910000
Nombre total d'octets en cours d'utilisation après un nettoyage56321191343323984637493054

Tableau 2. Résultats des performances de .NET Compact Framework pour une durée d'exécution du test de 100 minutes

Application gérée : temps d'activation de l'IST après une durée d'exécution de 100 minutes

Figure 4. Application gérée : temps d'activation de l'IST après une durée d'exécution de 100 minutes

La visionneuse de processus distants confirme le fait que le Garbage Collector et le compilateur JIT n'affectent pas le comportement en temps réel. La figure 5 illustre un vidage d'écran de la visionneuse de processus distants pour l'application gérée. Toutes les threads de l'application (sauf la thread en temps réel avec priorité 0) s'exécutent avec des priorités normales (251). Pendant nos mesures, le blocage du noyau ne s'est pas avéré nécessaire pour que le compilateur JIT et le Garbage Collector puissent réaliser leur travail.

Visionneuse de processus distants montrant l'application gérée

Figure 5. Visionneuse de processus distants montrant l'application gérée

Les pièges

Pendant le test, le fait d'accroître la fréquence du signal de blocage a généré des résultats inattendus dans l'application gérée. L'application a bloqué le système de façon aléatoire, surtout lorsque l'écran avait besoin d'être fréquemment redessiné parce que certaines de ses zones n'étaient pas valides. L'étude de ce problème a révélé un comportement qui a surpris les programmeurs Win32 les plus chevronnés. Dans une application Win32 , l'utilisation d'une horloge aboutit à un message WM_TIMER chaque fois que l'horloge arrive à expiration. Cependant, dans la file d'attente, les messages WM_TIMER sont des messages à priorité basse, qui ne sont envoyés que s'il n'y a pas d'autres messages dotés d'une priorité plus élevée à traiter. Ce comportement peut affecter la fiabilité de l'horloge, d'autant que CreateTimer ne fournit pas une horloge précise. Ce n'est pas un problème, surtout si l'horloge est utilisée pour mettre à jour une interface utilisateur graphique (GUI). Néanmoins, dans l'application gérée, nous utilisons un objet System.Threading.Timer pour créer une horloge. Cette horloge appelle un délégué chaque fois qu'elle arrive à expiration. Ce délégué est appelé depuis une thread séparée résidant dans un pool de threads. Si le système est trop occupé à d'autres activités (par exemple le retraçage de l'ensemble de l'écran), d'autres délégués de l'horloge sont activés, chacun dans une thread distincte, avant que les délégués précédemment activés n'en aient terminé. Cela peut aboutir à l'utilisation de toutes les threads disponibles dans le pool et provoquer le blocage du système. L'extrait 4 donne la solution pour éviter ce comportement. Chaque fois qu'un délégué de l'horloge est activé, nous interrompons l'objet horloge en appelant la méthode Change de l'objet Timer pour indiquer que nous ne voulons pas obtenir le message d'horloge suivant tant que nous n'aurons pas traité le message en cours. Cela peut générer des intervalles d'horloge imprécis. Dans notre cas l'horloge n'est utilisée que pour actualiser l'écran : cette imprécision ne constitue pas un problème.

La vérification des résultats

Pour pouvoir comparer les résultats de notre test aux résultats standard obtenus dans les mêmes conditions, nous avons également écrit une application Win32 appelant la même DLL et dotée de la fonctionnalité en temps réel. L'application Win32 est fonctionnellement identique à l'application gérée. Elle fournit au système une interface utilisateur graphique qui affiche les informations d'horloge dans une fenêtre. Cette application affiche les résultats de l'horloge à la réception des messages WM_TIMER en utilisant tout simplement les API Win32. Comme le montrent les figures 6 et 7, nous n'avons constaté aucune différence significative dans les performances. À la figure 6, la latence d'interruption est là encore mesurée à l'aide d'un oscilloscope. Pour l'application Win32, la latence est de 14,4 µs. Dans le pire des cas, la latence est de 55,2 µs, ce qui représente une instabilité de 40,8 µs. Ces résultats sont identiques à ceux obtenus lors du test exécuté avec l'application gérée .NET Compact Framework.

Application Win32 : temps d'activation de l'IST après une durée d'exécution de 10 minutes

Figure 6. Application Win32 : temps d'activation de l'IST après une durée d'exécution de 10 minutes

À la figure 7, les données périodiques s'affichent lors de l'activation de l'IST, là encore pour l'application Win32. Les résultats sont là aussi identiques à ceux obtenus avec une application gérée .NET Compact Framework. Vous pouvez télécharger les deux sources correspondant à l'application gérée et à l'application Win32.

Application Win32 : temps d'activation de l'IST après une durée d'exécution de 10 minutes

Figure 7. Application Win32 : temps d'activation de l'IST après une durée d'exécution de 10 minutes

Conclusion

Vous comprendrez qu'il n'est pas question d'utiliser .NET Compact Framework pour n'importe quel travail en temps réel. Ce que nous suggérons, c'est de l'utiliser comme couche de présentation. Dans un système de ce type, .NET Compact Framework peut « coexister pacifiquement » avec la fonctionnalité en temps réel, sans que cela affecte le comportement en temps réel de Windows CE .NET. Dans cet article, nous n'avons pas détaillé les capacités graphiques de .NET Compact Framework. Dans notre cas, nous n'avons trouvé aucune différence significative entre une application écrite entièrement en Win32 et une application en partie écrite en C# dans un environnement géré. Étant donné que .NET Compact Framework offre au programmeur une meilleure productivité et une grande richesse, il est très avantageux d'écrire les couches de présentation en code géré et d'utiliser du code non géré pour écrire les fonctionnalités en temps réel dur. Cette approche vous permet d'établir une distinction précise entre ces deux types de fonctionnalités.

Remerciements

Cela faisait longtemps que nous souhaitions tester l'utilité de .NET Compact Framework dans des scénarios en temps réel. Ce test n'a été possible que grâce à la coopération de personnes et d'entreprises qui nous ont fourni le matériel et les équipements de mesure appropriés. C'est pourquoi nous souhaitons remercier Willem Haring de Getronics pour son soutien, ses idées et son hospitalité pendant la réalisation du projet. Nous tenons aussi à remercier nos collègues de Delem pour nous avoir accueillis et nous avoir fourni l'équipement nécessaire à la réalisation des tests.

À propos des auteurs

Michel Verhagen travaille chez PTS Software aux Pays-Bas. Consultant Windows CE .NET, il compte quatre années d'expérience dans l'utilisation de Windows CE. Il est plus particulièrement spécialisé sur Platform Builder.

Maarten Struys travaille aussi pour PTS Software. Il est responsable du centre sur les compétences incorporées et en temps réel. Expert dans le développement Windows (CE), il utilise Windows CE depuis son lancement. Depuis 2000, Maarten travaille sur du code géré dans des environnements .NET. Il est aussi journaliste indépendant pour deux grands magazines néerlandais spécialisés dans le développement de systèmes embarqués. Il a récemment ouvert un site Web donnant des informations sur .NET en environnement embarqué.

Ressources supplémentaires

Pour plus d'informations sur CE .NET, veuillez consulter le site Web de Windows Embedded.

Pour obtenir la documentation en ligne et l'aide contextuelle de Windows CE .NET, veuillez consulter la documentation de Windows CE .NET (en anglais).

Pour plus d'informations sur Microsoft Visual Studio® .NET, veuillez consulter le site Web de Visual Studio.



Dernière mise à jour le mercredi 16 juillet 2003



Pour en savoir plus
Cela vous a-t-il été utile ?
(1500 caractères restants)
Merci pour vos suggestions.
Afficher:
© 2014 Microsoft. Tous droits réservés.