Problème avec BeginInvoke dans .NET
-
21-09-2019 - |
Question
Je le code suivant pour envoyer une requête à youtube et envoyer le résultat total à une zone de texte. Si je d'alerte pour le résultat, il est OK, mais je ne peux pas affecter le résultat à une zone de texte. S'il vous plaît me expliquer pourquoi?
private void SearchVideo(string keyword)
{
string orderBy = "";
switch (cboSortBy.SelectedIndex)
{
case 1: orderBy = "published"; break;
case 2: orderBy = "viewCount"; break;
case 3: orderBy = "rating"; break;
default: orderBy = "relevance"; break;
}
SearchDelegate sd = Search;
sd.BeginInvoke(keyword, orderBy, SearchCompleted, sd);
}
private void SearchCompleted(IAsyncResult ar)
{
if (null == ar) return;
SearchDelegate sd = ar.AsyncState as SearchDelegate;
Feed<Video> result = sd.EndInvoke(ar);
txtSearch.Text = result.TotalResults.ToString();
}
private Feed<Video> Search(string keyword, string orderBy)
{
YouTubeQuery query = new YouTubeQuery(YouTubeQuery.DefaultVideoUri);
query.OrderBy = orderBy;
query.Query = keyword;
query.SafeSearch = YouTubeQuery.SafeSearchValues.None;
return GetRequest().Get<Video>(query);
}
Et l'erreur
opération de la Croix-fil non valide: Contrôle « txtSearch » accessible à partir d'un fil autre que le fil était créé.
La solution
Vous appelez BeginInvoke
de sorte que votre délégué est invoquée sur un fil fil piscine. Vous ne pouvez pas accéder à l'interface de ce fil fil piscine; vous devez appeler Invoke
ou BeginInvoke
sur le contrôle d'utiliser ensuite les résultats sur le thread d'interface utilisateur. Par exemple, en utilisant une méthode anonyme:
txtSearch.BeginInvoke((MethodInvoker) delegate()
{ txtSearch.Text = result.TotalResults.ToString(); }
);
Ou en utilisant une expression lambda, et avec une variable locale séparée juste pour la clarté:
MethodInvoker action= () => { txtSearch.Text = result.TotalResults.ToString();};
txtSearch.BeginInvoke(action);
Utilisation Invoke
fera le bloc de thread appelant jusqu'à ce que le thread d'interface utilisateur a invoqué le délégué; BeginInvoke
est non bloquant.
EDIT: Si le problème est que result.TotalResults
est le bit qui prend beaucoup de temps, faire encore peu sur le fil de fond:
string text = result.TotalResults.ToString();
txtSearch.BeginInvoke((MethodInvoker) delegate() { txtSearch.Text = text; });
Autres conseils
Au lieu de Delegate.BeginInvoke
vous pouvez envisager d'utiliser un BackgroundWorker . Un BackgroundWorker déclenche l'événement RunWorkerCompleted
après qu'il ait fini, qui fonctionne dans le thread d'interface utilisateur , de sorte que vous pouvez mettre à jour l'interface utilisateur il.
Parce que l'accès aux commandes de formulaires ne sont pas thread-safe en soi, le débogueur vous avertit que vous enfreignez les règles en y accédant à partir d'un autre thread. Au lieu de cela, vous pouvez Le message d'erreur vous dit exactement ce que le problème est. Vous ne pouvez pas manipuler en toute sécurité les contrôles de l'interface utilisateur sur un thread différent de celui qui a créé le contrôle; le débogueur est conçu pour attraper ce (voir MSDN pour plus de détails). Alors, soit vous devez appeler Invoke
le contrôle directement pour obtenir les résultats que vous voulez. Il y a un grand, tutoriel Microsoft complète de la façon de le faire
BeginInvoke
sur le contrôle afin qu'il soit exécuté sur le thread d'interface utilisateur, ou vous avez besoin de mettre en place un mécanisme de communication entre le fil et invoqué le fil de l'interface utilisateur. Il est évident que l'ancien peut être accompli simplement avec TextBox.BeginInvoke
: txtSearch.BeginInvoke(sd, new object[] { keyword, orderBy, SearchCompleted });