Je suis au milieu d'une application de formulaire Windows C # viticole du marché via un algorithme (stratégie) pour créer des ordres à une entreprise de courtage. Que tout semble tester assez bien jusqu'à ce que j'ai essayé de construire à la capacité de gérer plusieurs stratégies simultanément avec chaque stratégie de son propre thread. À ce stade, tout commence à fonctionner de manière incorrecte. Je crois que j'ai des classes qui ne sont pas en sécurité qui conduisent un comportement erratique. N'importe quel point de vue sur la manière dont je peux enfiler cela sous une manière excessive est profondément appréciée!
La manière dont les citations sont introduites dans les algorithmes est la suivante: 1) Les événements de données de marché sont tirés du logiciel de courtiers à une classe client dans mon logiciel appelé ConnectionSstatus. Lorsque l'événement de données de marché est déclenché, un objet de devis est construit à partir des valeurs actuelles de ces variables statiques qui représentent une offre, demandent, etc. Une fois la citation de la citation, je m'efforce de l'envoyer dans chacun des algorithmes de stratégie qui fonctionnent. Voici le code que j'utilise pour faire cela: P>
public void NewIncomingQuote(object sender, DoWorkEventArgs e) { Quote QUOTE = e.Argument as Quote; lock (QuoteLocker) { manager.LiveQuote(QUOTE); priorQuote = QUOTE; } }
3 Réponses :
Le verrouillage doit se produire à la fois à la lecture et à l'écriture à n'importe quel état partagé. Sans serrures sur la lecture, le code peut toujours lire et écrire simultanément. P>
Vous pouvez envelopper la lecture et écrire le verrouillage dans le gestionnaire. P>
Merci pour la dépendance des commentaires. Dites-vous que à chaque point "en aval" du code qui lit l'objet de devis, j'ai besoin d'une serrure? (Il y a beaucoup d'endroits et d'objets en aval qui utilise des valeurs de chaque citation).
Emballage Les serrures dans le gestionnaire sont une manière dangereuse. Exemple. Si vous avez une lecture (verrouillée) et une écriture (verrouillée), cela se sent en sécurité. Mais si vous faites une lecture verrouillée (), une incrémentation, écrit verrouillée () Vous aurez toujours une violation de votre section critique. La serrure doit être autour de l'utilisation totale des données, ce qui entraîne une modification atomique de vos données.
@Spender Dans ce cas, il est préférable de copier la citation et d'éviter la période de verrouillage ... En fait, le verrouillage approprié rendrait ses stratégies de manière séquentielle, il rendrait l'effort multithreading totalement inutile et plus coûteux en raison de la surcharge du verrouillage + commutation de contexte.
Si: p>
1) Les stratégies sont invoquées via la méthode 2) Les modifications apportées à Vous devez créer une copie du ciote code> fourni avant appelant LiveQuote CODE> et peuvent modifier
citation code> instances. P>
de citation code> ne doivent pas être partagées entre stratégies. P>
livequote () code> et envoyez la copie dans la méthode de stratégie plutôt que la citation d'origine. Selon d'autres exigences, vous n'avez peut-être pas besoin de verrouillage du tout. P>
Il y a deux choses qui se produisent dans votre code:
1. Vous avez reçu une citation d'un fil (le producteur alias le flux de données de marché).
2. Vous envoyez la citation à un autre fil (le consommateur AKA StratégieSembler).
À ce stade, il existe une affirmation sur la citation, en d'autres termes, le fil du producteur et chaque thread de consommateur strong> (c'est-à-dire chaque instance d'une stratégie) peut modifier la citation que vous venez de le fournir. Pour que vous puissiez supprimer la conflit, vous devez faire l'une des trois choses: p> Pour votre cas, je vous suggère de prendre la troisième option car le verrouillage est plus cher que de copier une citation (j'espère que vos devis ne sont pas vraiment gros) ... L'option deux est également bonne, mais votre stratégie devrait Ne pas modifier la citation. p> En donnant à chaque consommateur une copie de la citation, vous garantissez qu'ils ne partagent aucune donnée, aucun autre thread ne modifiera la citation et vous éliminerez la conflit. Si vos stratégies ne créent aucun autre thread, vous avez terminé. P> En général, vous devriez éviter de verrouiller et vous devriez essayer de minimiser le partage des données, mais si vous Mise à jour par commentaires: strong>
Si vous laissez le code tel quel (ce qui signifie que vous fournissez une copie de la citation de chaque fil), je ne vois pas pourquoi vos autres stratégies ne recevront pas la citation avant la fin de la première stratégie ... Votre première stratégie sera la plus Commencez probablement à travailler pendant la création des threads des autres stratégies. L'ensemble du point de faire fonctionner vos stratégies dans un thread séparé est d'éviter exactement ce problème ... vous démarrez un nouveau thread afin que vos autres stratégies n'attendent pas l'autre sur l'autre. P> Cette partie de Le code sera probablement complet avant même que tous vos threads ne commencent à travailler ... p> est votre aliment de marché modifiant la citation réelle pendant que vous créez les threads? Les aliments du marché fournissent généralement des instantanés, alors à moins que quelque chose change votre devis pendant que vous effectuez les threads, la conception ci-dessus devrait être très bien. S'il y a un problème avec la conception, je peux vous donner un producteur et une conception de plusieurs consommateurs basée sur une file d'attente de blocage qui est également très efficace ( Vous pouvez consulter cette discussion pour une idée sur la façon dont cela fonctionne et je peux vous dire comment la modifier pour votre spécifique exemple). P> p>
Ou li>
Ou li>
Pour que vos stratégies soient synchronisées correctement, ils doivent être Quelocker code> objet, i.e.
queuléocker code> doit être visible à chaque fil. Même si vous le faites correctement et que vous rendez vos stratégies synchronisez (verrouillez sur le
queuléoker code>), vous ne pouvez aussi bien pas avoir de threads ... vous exécutez la surcharge de la commutation de contexte + verrouillage et vos stratégies sera exécuté séquentiellement pour la même citation. p>
Oui, la prévention est meilleure que la fixation. Si vos threads peuvent exécuter indépendamment (option 3), vous aurez l'option la plus sûre et la meilleure performance. Comme vous ne pouvez pas garantir que la première citation est entièrement traitée en premier, vous devrez peut-être ajouter une horodatage (haute résolution) dans votre copie.
Merci pour la rétroaction Lirik. Que tout fait un tas de sens. L'option est la plus sensée pour moi pour les raisons que vous avez mentionnées et d'autres aussi. En termes de copie et de transmettre cela à chaque instance d'assembleur, je pense que cela serait préférable avec un événement afin que tous les assembleurs reçoivent leur copie de la citation simulatinée et peuvent ensuite traiter leur logique. Sinon, si elle est laissée dans la boucle, les stratégies suivantes n'obtiendront pas la citation tant que la 1ère unique complète sa logique (environ 100 ms et les citations peuvent arriver aussi peu que 50 ms).
Si je mettais la citation dans un événement que chaque assembleur se redonne à, quelle est la meilleure façon d'obtenir chaque assembleur sur un thread afin que je puisse tirer parti de plus de noyaux sur la CPU? Pour le moment, chacun de ces assembleurs et l'algorithme de stratégie que chacun a créé toutes la course sur le fil principal
Merci pour l'Insight Lirik, je vais essayer cette version et voir comment elle est passée avec la copie de la citation. En termes de changement de marché, l'extrait de code ci-dessus est à l'intérieur de la méthode Bandequote (). Cette méthode est appelée chaque fois qu'il y a un changement à Bidprice, Bidvolume, Askprice ou Askvolume. Chaque fois qu'il est appelé, une nouvelle citation est créée à l'aide de variables statiques modifiées par l'événement de données de courtier. Je ne sais pas si un producteur et une conception de consommateurs multiples seraient un meilleur moyen de le faire. Je vais jeter un oeil au lien que vous avez posté - merci!
Vous créez une nouvelle citation, mais remplacez-vous celui qui est actuellement utilisé pour initialiser les threads?
1) Qu'est-ce que
manager.livequote () code> faire? Est-ce l'interface avec vos méthodes de stratégie? 2) Est-ce que
queelocker code> a
StratégieSembler code> Membre d'instance? 3) Enfin, quel type de comportement attendez-vous et que voyez-vous?
1) gestionnaire.livequote () est la méthode que a) ajoute le devis dans une liste référencée par des classes d'indicateurs et la classe d'algorithme de stratégie et la classe de simulation de commande b) transmet que la citation dans le stratégiegorithme c) gère l'ordre des objets dans la stratégie. Simuler la latence de remplissage / annulation de commande / de communication, etc.
2) Quelocker est un objet statique objet statique quoelocker = nouvel objet ();
3) En termes de comportement, je m'attends à chaque citation car elle arrive à être ajoutée à chaque instance de stratégie de stratégie et à partir de là ajouté au gestionnaire.livequote. De là, la stratégie traverse son algorithme pour prendre la décision d'acheter / vendre / ne rien faire. Chaque instance d'assembleur n'a qu'une instance StratégieManager et chaque instance StratégieManager n'a qu'une classe de stratégie qu'elle passe de citations et gère les commandes. Le comportement que je constate a une seule citation ajoutée 1 ou 2 ou 3 ou 4 fois à une instance d'assembleur donnée et non ajoutée du tout à une instance d'assembleur différente.
Est-ce que l'une des suggestions fonctionnait pour vous?
Avez-vous trouvé un moyen de gérer les threads multiples de manière sûre?