J'écris une bibliothèque en C #, mais j'ai besoin de le rendre asynchrone. Normalement, vous exposez un ensemble de fonctions DLL, et ils prennent des paramètres d'entrée et renvoient une valeur lorsque vous avez terminé. Mais comment puis-je faire une fonction de bibliothèque (appelable à partir de C ++ / Delphi / etc.) qui commence déjà à diffuser la sortie en flux tout en prenant une entrée? P>
La seule solution que je vois maintenant est de communiquer à l'aide de sockets / tuyaux / etc., au lieu des appels DLL. p>
Quelqu'un a-t-il un exemple comment faire cela avec des appels DLL normaux? p>
6 Réponses :
Un bon modèle pour un appel de bibliothèque asynchrone simple (qui est situé dans Votre bibliothèque pourrait également fournir une méthode system.dll code>) est webclient.downloadstringasync . Cette méthode télécharge à partir d'un
uri code> asynchrone et soulève le DownloadStringCompletd événement chaque fois qu'il finit. p>
FOOASYNC code>, qui ne bloque pas le thread actuel, mais soulève un événement code> alimentataReceedecevivisé chaque fois que certaines données sont entrées dans votre bibliothèque et un
FOOCOMPOMPLETED code> événement chaque fois que le calcul finit. P>
Cela ne tient pas compte du fait que l'OP veut que les deux directions ASYNC - c'est-à-dire tout en donnant une sortie de rendement, il souhaite recevoir également la contribution ASYNC ...
Il y a quelques façons d'aller avec cela. Dans la plupart des langues, vous pouvez faire des appels ASYNC à des méthodes à l'aide de threads ou de répartiteurs. En général, tant que vous faites votre dll ré-entrant (capable d'entretenir Plusieurs threads en même temps) L'environnement appelant peut prendre soin de la partie ASYNC. P>
Il est possible de faire cuire les appels ASYNC dans votre API. Un exemple de quelque chose qui fait ceci est le proxy client WCF a >. P>
Microsoft a un bon article sur cette question. Si vous venez de déménager sur l'endinvoke, cela devrait également fonctionner pour vous. http://msdn.microsoft.com/fr- US / Bibliothèque / 2E08F6YC (v = vs.71) .aspx p>
puisque vous voulez que les deux entrées et la sortie soient asynchrones, vous aurez besoin d'un Fil de travailleur: Si ni le fil d'entrée, ni celui qui prend La sortie peut être bloquée, les deux ne peuvent pas être dérangées pour faire le travail. P> li>
Votre déjà pensé de communiquer via des tuyaux, mais pourquoi utiliser un tuyau et non une stricture interne? P> Li>
Vous avez donc cette file d'attente sans verrouillage sur l'entrée, une autre sur la sortie et un thread de travailleur p> li>
Le thread du travailleur prend la saisie de l'inquée, le traite, le met dans l'Outquue P> Li>
Si la file d'attente d'entrée devient vide, le thread du travailleur n'a rien à reprocher, alors il soulève un événement "Besoin plus de données" puis des blocs sur la file d'attente d'entrée devenant (en partie) pleine p> li>
Si le fil du travailleur met quelque chose dans la file d'attente de sortie, il pose un événement "avoir plus de données", et si la file d'attente de sortie devient (entièrement) pleine, il bloque l'espace de sortie devenant disponible p> < / li>
Votre API est non bloquante: ni l'envoi d'une entrée ni de sortie de sortie jamais bloquée p> li>
Votre API est async: les notifications (via des événements) sont données p> li> ul>
Selon les commentaires de l'OP L'application d'appel envoie audio à la DLL, la DLL envoie audio via une interface USB, la DLL capture de l'audio à partir de l'interface MIC et doit renvoyer l'audio capturé à l'application alors que L'application envoie audio à la DLL, etc. p>
basé sur cela et le fait que l'appel peut être écrit dans Rahter différentes langues, je vois des options pour les canaux de communication: P>
J'aime l'approche suivante car cela rend vraiment simple pour les clients.
// your library class Foo { public event EventHandler ComputeCompleted = (sender, e) => { }; public void Compute() { // kick off work on a background thread // possibly using the BackgroundWorker object var bw = new BackgroundWorker(); bw.RunWorkerCompleted += RunWorkerCompleted; bw.RunWorkerAsync(); } private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { ComputeCompleted(this, new object()); } } // calling code Foo foo = new Foo(); foo.ComputeCompleted += Completed; foo.Compute(); private void Completed(object Sender, EventArgs e) { // process the result here }
Et comment cela fonctionnerait-il avec / être utilisable de C ++, Delphes, etc.?
@Yahia C ++ a un mode géré qui peut facilement consommer des constructions C #. Je n'ai rien fait avec Delphi depuis 1995, alors je ne pouvais donc pas vraiment commenter cela.
Oui, mais je comprends de l'OP que la bibliothèque doit être utilisable de plusieurs langues, y compris des natifs (Delphi est indigène mais peut utiliser COM par exemple) ...
@Yahia Dans ce cas, la bibliothèque doit être marquée comme COM-VISIBLE CODE> et l'OP doit exécuter
regasm code> l'outil pour l'enregistrer dans le registre. L'OP devrait alors être en mesure d'accéder à l'événement en tant qu'évoilement COMMAND.
Je devrais également souligner que l'OP veut Exposer un ensemble de fonctions DLL CODE>, que .NET Framework n'est même pas capable de faire. Je pense donc que cela est aussi bon que possible en ce qui concerne une bibliothèque comme celle-là avec .NET.
Cela dépend vraiment de ce que vous faites ... Vous pouvez même échanger des pointeurs sur des fonctions qui sont appelées par votre DLL lorsqu'il a besoin de la prochaine partie de l'entrée ... Vous pouvez utiliser la mémoire partagée plus un mutex global pour échanger des données d'avancez Etc. Pour obtenir une réponse utile, vous devez vraiment donner beaucoup plus de détails ...
Il existe différentes options différentes ici - cela dépend en partie de quel type de sortie que vous avez eu et à quoi ressemble l'entrée. Lorsque j'entends "asynchrone", j'ai tendance à penser à des appels asynchrones à un coup, "Voici quelques entrées, donnez-moi une sortie lorsque vous êtes prêt" mais cela sonne comme si vous souhaitez peut-être plus d'une solution de streaming. Plus de contexte serait utile.
Une option est que vos fonctions prennent un paramètre de rappel que vous pouvez appeler pour traiter les morceaux des données de sortie. Ce n'est peut-être pas le plus pratique des API. Un moyen plus naturel de retournerait une sorte d'objet itérateur, mais cela faciliterait la mise en œuvre de votre bibliothèque plus compliquée, en fonction de l'emplacement de son entrée.
Cela ressemble plus à votre recherche en temps réel et non asynchronisé; Si tel est le cas, vous devrez utiliser des sockets.
Avez-vous résolu le problème des fonctions de bibliothèque .NET à partir du code natif?
Alors, quel est ce programme?
@Yahia C'est un pilote de périphérique, vous pouvez diffuser audio (ligne de sortie) et renvoie l'audio enregistré (ligne in). La mémoire partagée / mutexes semble très compliquée pour les utilisateurs finaux qui tentent d'utiliser ce pilote.
@Joshua Un pilote de périphérique fonctionne généralement en mode noyau si je ne me trompe pas ... et les pilotes en mode noyau ne peuvent pas être écrits en C # Afaik ...
@Jonskeet C'est vraiment très similaire à la diffusion en continu, mais la différence est que chaque échantillon d'entrée dans le flux d'entrée entraînera un caractère de sortie dans le flux de sortie, il s'agit donc de streaming bidirectionnel.
@Yahia Ce n'est pas un "vrai" pilote de périphérique, c'est juste une bibliothèque de communication avec un morceau de matériel que j'ai développé. Écrire un pilote au niveau du noyau pour ce serait beaucoup plus de travail qu'un simple dll qui accomplit la même chose?
@Joshua L'entrée provient-elle du matériel et la sortie va à une application?
@Yahia Non, une application m'envoie de l'audio, mon matériel envoie ce audio numériquement à un amplificateur, tout en capture le flux de sortie (microphone). Le problème est que l'application d'envoi doit adapter son audio sortant au signal de microphone reçu. Par exemple: ils doivent pouvoir effectuer une FFT sur les données du microphone et modifier l'audio de sortie en fonction des résultats, tout en conservant la lecture sans jet.
@Joshua maintenant c'est un peu de contexte ... veuillez mettre à jour votre question avec cette information ... et vérifiez ma réponse ci-dessous ...