Je travaille avec un périphérique externe dont je reçois des données. Je veux gérer sa file d'attente de lecture/écriture de données de manière asynchrone, dans un thread. Je l'ai surtout fait fonctionner: Il y a une classe qui gère simplement les deux flux, en utilisant le NSStreamDelegate
pour répondre aux données entrantes, et en répondant à NSStreamEventHasSpaceAvailable pour envoyer des données qui attendent dans un tampon après avoir échoué à envoyer plus tôt.E/S asynchrones NSStream avec GCD
Cette classe, appelons-la SerialIOStream
, ne connaît pas les threads ou les files d'attente GCD. Au lieu de cela, son utilisateur, appelons-le DeviceCommunicator
, utilise une file d'attente GCD dans laquelle il initialise la classe SerialIOStream
(qui à son tour crée et ouvre les ruisseaux, les programmer dans le runloop en cours):
ioQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(ioQueue, ^{
ioStreams = [[SerialIOStream alloc] initWithPath:[@"/dev/tty.mydevice"]];
[[NSRunLoop currentRunLoop] run];
});
De cette façon, la SerialIOStream
La méthode stream:handleEvent:
s'exécute dans cette file d'attente GCD, apparemment.
Toutefois, cela provoque certains problèmes. Je crois que je rencontre des problèmes de concurrence, jusqu'à obtenir des plantages, principalement au moment de l'alimentation des données en attente dans le flux de sortie. Il y a un élément essentiel dans le code où je passe les données de sortie mises en mémoire tampon au courant, puis voir la quantité de données effectivement accepté dans le cours d'eau, puis retirer cette partie de mon tampon:
NSInteger n = self.dataToWrite.length;
if (n > 0 && stream.hasSpaceAvailable) {
NSInteger bytesWritten = [stream write:self.dataToWrite.bytes maxLength:n];
if (bytesWritten > 0) {
[self.dataToWrite replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
}
}
Le code ci-dessus peut se dit de deux endroits:
- de l'utilisateur (
DeviceCommunicator
) - de la méthode
stream:handleEvent:
locale, après avoir été informé qu'il ya un espace dans le flux de sortie.
Ceux qui peuvent être (bien, sûrement) s'exécutent dans un thread séparé, et donc je dois m'assurer qu'ils n'exécutent pas ce code simultanément.
Je pensais résoudre ce problème en utilisant le code suivant dans DeviceCommunicator
lors de l'envoi de nouvelles données sur:
dispatch_async (ioQueue, ^{
[ioStreams writeData:data];
});
(writeData
ajoute les données à dataToWrite
, voir ci-dessus, et exécute ensuite le code ci-dessus qui envoie Cependant, cela ne fonctionne pas, apparemment parce que ioQueue est une file d'attente concurrente, qui peut décider d'utiliser n'importe quel thread disponible, et par conséquent conduire à une condition de concurrence lorsque writeData
est appelée par le DeviceCommunicator
pendant que writeData
il y a aussi un appel à partir de stream:handleEvent:
, sur des filetages séparés. Donc, je suppose que je mélange les attentes des threads (que je connais un peu plus) à mes apparents malentendus avec les files d'attente GCD.
Comment résoudre cela correctement?
Je pourrais ajouter un NSLock, en protégeant la méthode writeData avec elle, et je crois que cela résoudrait le problème à cet endroit. Mais je ne suis pas si sûr que c'est comme ça que GCD est censé être utilisé - j'ai l'impression que ce serait un jeu d'enfant.Dois-je plutôt faire une classe séparée, en utilisant sa propre file d'attente série, pour accéder et modifier le tampon dataToWrite
, peut-être? J'essaye toujours de saisir les modèles qui sont impliqués avec ceci. D'une certaine façon, cela ressemble à un modèle classique de producteur/consommateur, mais à deux niveaux, et je ne le fais pas correctement.
Huh - Je me demande si https://stackoverflow.com/a/31317588/43615 est la réponse à ce que je cherche. –