Comment initialiser un objet à l'aide asynchrone attendent modèle
Je suis en train de suivre RAII dans ma classes de service, ce qui signifie que lorsqu'un objet est construit, il est complètement initialisé. Cependant, je suis confronté à des difficultés avec des Api asynchrones. La structure de la classe en question ressemble à ce qui suit
class ServiceProvider : IServiceProvider //Is only used through this interface
{
public int ImportantValue { get; set; }
public event EventHandler ImportantValueUpdated;
public ServiceProvider(IDependency1 dep1, IDependency2 dep2)
{
//IDependency1 provide an input value to calculate ImportantValue
//IDependency2 provide an async algorithm to calculate ImportantValue
}
}
Je suis également visant à se débarrasser des effets secondaires ImportantValue
de lecture, pour en faire thread-safe.
Maintenant, les utilisateurs de ServiceProvider
va créer une instance de celle-ci, s'inscrire à un événement de ImportantValue
changement, et d'obtenir la première ImportantValue
. Et c'est là le problème, avec la valeur initiale. Depuis le ImportantValue
est calculé de manière asynchrone, la classe ne peut pas être entièrement initialisé dans le constructeur. C'est peut-être bien d'avoir une valeur nulle au départ, mais ensuite j'ai besoin d'avoir un endroit où il sera calculé première fois. Un endroit naturel pour que pourrait être le ImportantValue
's de lecture, mais je suis ciblage pour le rendre thread-safe et sans effets secondaires.
Donc, je suis tout bonnement avec ces contradictions. Pourriez-vous svp m'aider et offrir des alternatives? Avoir de la valeur initialisée dans le constructeur, tandis que nice n'est pas vraiment nécessaire, mais pas d'effets secondaires et fil-sécurité des biens est obligatoire.
Merci d'avance.
EDIT: Une chose de plus à ajouter. Je suis en utilisant Ninject pour l'instanciation, et comme je le comprends, il ne supporte pas les méthodes asynchrones pour créer une liaison. Alors que l'approche de lancer une Tâche de base de fonctionnement dans le constructeur de travail, je ne peux pas attendre son résultat.
I. e. deux approches suivantes (offert en guise de réponses à ce jour) ne compile pas, étant donné que la Tâche est retourné, c'est pas mon objet:
Kernel.Bind<IServiceProvider>().ToMethod(async ctx => await ServiceProvider.CreateAsync())
ou
Kernel.Bind<IServiceProvider>().ToMethod(async ctx =>
{
var sp = new ServiceProvider();
await sp.InitializeAsync();
})
Liaison Simple de fonctionner, mais je ne suis pas attendre le résultat de l'initialisation asynchrone a commencé dans le constructeur, tel que proposé par Stephen Cleary:
Kernel.Bind<IServiceProvider>().To<ServiceProvider>();
... et ce n'est pas très bon pour moi.
Découvrez ma bibliothèque AsyncContainer
OriginalL'auteur Haspemulator | 2013-04-09
Vous devez vous connecter pour publier un commentaire.
J'ai un blog qui décrit plusieurs approches pour
async
construction.Je recommande asynchrone usine méthode décrite par Reed, mais parfois, c'est pas possible (par exemple, l'injection de dépendance). Dans ces cas, vous pouvez utiliser une initialisation asynchrone modèle comme ceci:
Vous pouvez alors construire le type normalement, mais gardez à l'esprit que la construction seulement commence l'initialisation asynchrone. Quand vous avez besoin le type à être initialisé, votre code peut faire:
Noter que si
Initialization
est déjà terminée, l'exécution (de façon synchrone) se poursuit après laawait
.Si vous souhaitez une réelle propriété asynchrone, j'ai un blog pour que, trop. Votre situation sons comme il peut bénéficier de
AsyncLazy<T>
:OriginalL'auteur Stephen Cleary
Une possibilité serait de passer à une méthode de fabrique, au lieu d'utiliser un constructeur.
Votre usine méthode pourrait alors retourner un
Task<ServiceProvider>
, qui vous permettra d'effectuer l'initialisation asynchrone, mais pas de retour à la construitServiceProvider
jusqu'àImportantValue
a été (de manière asynchrone) calculé.Ce serait permettre à vos utilisateurs d'écrire du code comme:
Dans ce cas, il faut toujours avoir une usine méthode quelque part (vous ne pouvez pas construire une interface) - qui rend cela plus facile...
Avez-vous un exemple de ce que l'CreateAsync ressemblerait
OriginalL'auteur Reed Copsey
Vous pouvez utiliser mon AsyncContainer conteneur IoC, qui soutient exactement le même scénario que vous.
Il prend également en charge d'autres pratique des scénarios tels que async initialiseurs, au moment de l'exécution conditionnelle d'usines, dépendent async et de la synchronisation de l'usine de fonctions
OriginalL'auteur rafael
C'est une légère modification de @StephenCleary modèle de l'initialisation asynchrone.
La différence étant que l'appelant n'a pas besoin de "se souvenir" de
await
laInitializationTask
, ou même de savoir quelque chose au sujet de lainitializationTask
(en fait, il est maintenant changé pour le privé).La façon dont cela fonctionne est que, dans chaque méthode qui utilise les données initialisées il y a un appel initial à
await _initializationTask
. Cela renvoie instantanément la deuxième fois, parce que la_initializationTask
objet lui-même ont une valeur booléenne définie (IsCompleted
qui 'attendent mécanisme de" chèques) - alors ne vous inquiétez pas à ce sujet de l'initialisation plusieurs fois.Le seul hic, je suis conscient, c'est que vous ne devez pas oublier de l'appeler dans chaque méthode qui utilise les données.
La chose la plus importante dans ma réponse (qui n'était pas évident pour moi à un moment), c'est la réalisation que vous pouvez garder une référence à une tâche et que la tâche ne s'exécute qu'une fois et quel que soit le résultat est mis en cache, donc quelle que soit la méthode que vous appelez de la chaîne va essayer d'accéder à la tâche et si il y a une exception, elle sera juste une sorte de magie bulle à chaque fois.
Ok, j'ai fini par envelopper les attend dans un GuardInit (), qui attend, mais vérifie également le résultat si c'était fini, permettant de réessayer, ou la création d'une application échouer en fonction de la gravité de la conséquence. Merci pour la réponse.
Sûr 🙂 vous bénéficiez d'une longue phrase quand il est tapé sur mon téléphone! J'aime l'idée de l'auto retry - je suppose que cela dépend de ce que sa fait.
OriginalL'auteur Simon_Weaver
Je sais que c'est une vieille question, mais c'est le premier qui apparaît sur Google et, franchement, la accepté de répondre est une mauvaise réponse. Vous ne devez jamais forcer un retard de sorte que vous pouvez utiliser l'attendent de l'opérateur.
Une meilleure approche pour une méthode d'initialisation:
Cela utiliser le async cadre d'initialiser un objet, mais ensuite elle retourne une valeur booléenne.
Pourquoi est-ce une meilleure approche? Tout d'abord, vous n'êtes pas forcer un retard dans votre code qui à mon humble avis totalement défait le but de l'utilisation de l'async cadre. Deuxièmement, c'est une bonne règle de pouce à retourner quelque chose à partir d'une méthode asynchrone. De cette façon, vous savez si votre méthode async effectivement travaillées/est-ce qu'il était censé. De retour juste Tâche est l'équivalent de retour void sur une non-méthode async.
await Task.Delay
dans ma réponse est un espace réservé pour "faire le travail asynchrone ici pour initialiser l'instance". Il n'y aurait pasTask.Delay
dans le code réel.Je pense que votre réponse pire depuis que vous avez omis de préciser ce que l'on fait et ce que vous montrez, c'est que vous devriez être en forçant les délais pour obtenir async comportement lors de l'initialisation et de la construction. Vous devez garder à l'esprit que tout le monde à partir de différents niveaux d'expérience peuvent voir votre post et que vous avez donné la peine toutes les infos utiles de votre réponse et a démontré un mauvais codage sans clarté. En d'autres termes, quelqu'un de la marque nouvelle à l'async cadre permettrait de voir votre réponse et de mettre en œuvre des retards dans leur code pour obtenir asynchrone de l'initialisation et de la construction.
vous êtes tous les deux 😉
OriginalL'auteur John Doeses