1

J'utilise une bibliothèque tierce pour communiquer les données d'un périphérique d'entrée tiers vers un Windows Form. Ce que je cherche à faire est de recueillir des données d'entrée à partir de l'appareil, le traiter et, sous certaines conditions, faire rapport sur le fil de l'interface utilisateur de Windows ce qui se passe. Je n'ai pas accès au code source de la DLL tierce mais je sais que la méthode principale est sur un processus en arrière-plan et que je ne peux pas communiquer mes résultats sur le thread principal de l'interface utilisateur parce que je ne l'ai pas créé?La bibliothèque multithread utilisant le thread de travail ne peut pas communiquer avec l'interface utilisateur Thread

Windows Form:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 

     // create instance of my listener 
     MyListener listener = new MyListener(this); 
     Controller controller = new Controller(listener); 
    } 
} 

MyListener classe qui étend la 3e classe du parti Listener:

public class MyListener : Listener 
{ 
    public Form1 form; 
    private Frame frame; 

    // overloaded constructor 
    public LeapListener(Form1 f) 
    { 
     form = f; 
    } 

    /// <summary> 
    /// onFrame is the main method that runs every milisecond to gather relevant information 
    /// </summary> 
    public override void onFrame(Controller controller) 
    { 
     // Get the most recent frame and report some basic information 
     frame = controller.frame(); 
    } 
} 

Le problème est que je peux communiquer revenir au thread principal de l'interface utilisateur de partout dans la classe de MyListener mais Je ne peux pas communiquer à partir de la méthode onFrame car il s'exécute sur un thread d'arrière-plan. Y a-t-il de toute façon une prise en main du thread principal à partir d'un thread d'arrière-plan que je n'ai pas créé? J'ai essayé ReportProgress, j'ai essayé de créer un événement sur MyListener et toutes les tentatives de parler au thread principal de l'interface utilisateur de onFrame plantent l'application et me donnent des erreurs d'emplacement de mémoire invalides.

Toute aide serait grandement appréciée.

+0

'Y a-t-il un moyen d'obtenir le thread principal à partir d'un thread d'arrière-plan que je n'ai pas créé?' - Bien sûr, le problème en suspens est ce que vous avez dans votre C++/C#/quoi que ce soit pour le faire. En règle générale, il s'agit des méthodes Invoke/BeginInvoke fournies par le langage/bibliothèque ou des API SendMessage/PostMessage. Wot vous avez? –

+0

Est-ce que 'Controller' l'objet s'exécute dans un thread différent? – didierc

Répondre

0

Habituellement, essayer d'accéder à un objet UI à partir d'un thread autre que celui qui gère l'interface utilisateur est problématique. Ce n'est pas un problème de Windows seulement, mais un modèle plus général.

La solution habituelle consiste à configurer une forme de mécanisme de propagation d'événement, qui contiendrait les données nécessaires pour mettre à jour l'interface utilisateur, et laisser le thread principal gérer cette tâche. Appelons l'interface utilisateur du thread d'interface utilisateur et le thread d'arrière-plan BT.

Vous pouvez avoir une fonction qui envoie un événement à l'interface utilisateur de BT, puis bloque BT jusqu'à ce que l'événement soit traité par l'interface utilisateur. C'est un système simple utilisant un sémaphore pour bloquer BT jusqu'à ce que l'interface utilisateur le lâche. L'avantage d'un tel système est qu'il est simple, vous n'avez pas besoin de gérer plus d'un événement à la fois de chaque thread. L'inconvénient est que si les événements prennent beaucoup de temps à être traités, l'application aurait une très mauvaise résolution d'échantillonnage de l'appareil. Un moyen alternatif (et meilleur) consiste à créer une file d'attente d'événements, de publier des messages BT et d'interroger l'interface utilisateur pour la mettre à jour automatiquement. Cela demande un peu plus de travail, mais c'est beaucoup plus résilient pour le long timing de l'interface utilisateur. Pour cette seconde technique, vous devez établir un format pour votre événement, configurer une file d'attente partagée et mutex entre BT et UI, et le reste devrait être facile à faire.

La file d'attente pourrait être quelque chose comme ce qui suit:

#include <queue> 


template <typename EVENT> 
class EventQueue { 
    protected: 
    typedef EventQueue<EVENT> self_type; 
    typedef std::queue<EVENT> queue_type; 
    Mutex m_; 
    queue_type q_; 
    void lock(){ 
     // lock the mutex 
    } 
    void unlock(){ 
     // unlock the mutex 
    } 
    public: 
    EventQueue() { 
     // initialize mutex 
    } 
    ~EventQueue() { 
     // destroy mutex 
    } 

    void push(EVENT &e){ 
     lock(); 
     q_.push(e); 
     unlock(); 
    } 
    EVENT &pop(){ 
     EVENT &e; 
     lock(); 
     e = q_.pop(); 
     unlock(); 
     return e; 
    } 
    int size(){ 
     int i; 
     lock(); 
     i = q_.size(); 
     unlock(); 
     return i; 
    } 
}; 

Ceci est très simple que vous pouvez le voir, il vous suffit d'utiliser le modèle ci-dessus avec tout ce dont vous avez besoin classe d'événements.

J'ai oublié le code qui gère le mutex, cela dépend de l'API sur laquelle vous voulez compter. Comme indiqué ci-dessus, l'interface utilisateur doit uniquement interroger la file d'attente, c'est-à-dire vérifier la taille de la file d'attente et supprimer un événement s'il y en a un disponible. Du côté BT, tout ce que vous devez faire est d'inclure l'appel push de l'événement dans votre classe MyListener, au lieu d'accéder directement au formulaire.

Cette méthode est très efficace pour laisser les deux fils travailler ensemble sans marcher les uns sur les autres orteils.

Questions connexes