Comment annuler une Tâche d'attendre après un délai d'attente
Je suis en utilisant cette méthode pour instancier un navigateur web par programme, accédez à une url et retourne le résultat lorsque le document est terminé.
Comment pourrais-je être en mesure d'arrêter la Task
et ont GetFinalUrl()
retour null
si le document prend plus de 5 secondes à charger?
J'ai vu de nombreux exemples à l'aide d'un TaskFactory
mais je n'ai pas été en mesure de l'appliquer à ce code.
private Uri GetFinalUrl(PortalMerchant portalMerchant)
{
SetBrowserFeatureControl();
Uri finalUri = null;
if (string.IsNullOrEmpty(portalMerchant.Url))
{
return null;
}
Uri trackingUrl = new Uri(portalMerchant.Url);
var task = MessageLoopWorker.Run(DoWorkAsync, trackingUrl);
task.Wait();
if (!String.IsNullOrEmpty(task.Result.ToString()))
{
return new Uri(task.Result.ToString());
}
else
{
throw new Exception("Parsing Failed");
}
}
//by Noseratio - http://stackoverflow.com/users/1768303/noseratio
static async Task<object> DoWorkAsync(object[] args)
{
_threadCount++;
Console.WriteLine("Thread count:" + _threadCount);
Uri retVal = null;
var wb = new WebBrowser();
wb.ScriptErrorsSuppressed = true;
TaskCompletionSource<bool> tcs = null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) => tcs.TrySetResult(true);
foreach (var url in args)
{
tcs = new TaskCompletionSource<bool>();
wb.DocumentCompleted += documentCompletedHandler;
try
{
wb.Navigate(url.ToString());
await tcs.Task;
}
finally
{
wb.DocumentCompleted -= documentCompletedHandler;
}
retVal = wb.Url;
wb.Dispose();
return retVal;
}
return null;
}
public static class MessageLoopWorker
{
#region Public static methods
public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
EventHandler idleHandler = null;
idleHandler = async (s, e) =>
{
//handle Application.Idle just once
Application.Idle -= idleHandler;
//return to the message loop
await Task.Yield();
//and continue asynchronously
//propogate the result or exception
try
{
var result = await worker(args);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
//signal to exit the message loop
//Application.Run will exit at this point
Application.ExitThread();
};
//handle Application.Idle just once
//to make sure we're inside the message loop
//and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
//set STA model for the new thread
thread.SetApartmentState(ApartmentState.STA);
//start the thread and await for the task
thread.Start();
try
{
return await tcs.Task;
}
finally
{
thread.Join();
}
}
#endregion
}
- Agréable de voir quelqu'un qui est réellement à l'aide de ce code :), j'ai un autre exemple qui fait une chose similaire avec un délai d'attente: stackoverflow.com/a/21152965/1768303. Recherchez var
cts = new CancellationTokenSource(30000)
. - Merci. Avez-vous un exemple de la façon de faire dans une application console, par hasard? Aussi je ne pense pas que webBrowser peut être une variable de classe parce que je suis en cours d'exécution le tout dans un parallèle pour chacun, le parcours des milliers d'URLs
- J'ai utilisé le code que vous avez suggéré dans mon application console et a obtenu: Système.Le filetage.ThreadStateException: contrôle ActiveX '8856f961-340a-11d0-a96b-00c04fd705a2' ne peut pas être instanciée car le thread actuel n'est pas en single-threaded apartment. Qui, j'imagine, est-ce que la boucle de message thread de travail dans votre autre exemple de code. Qui est ce que je ne pourrais pas obtenir de travail avec le cancellationToken. Aide appréciée. Je vais continuer d'essayer.
- Il semble que non seulement il a besoin pour s'exécuter sur un thread STA, mais aussi besoin d'une boucle de message travailleur à: stackoverflow.com/a/19737374/1768303
Vous devez vous connecter pour publier un commentaire.
Mis à jour: la dernière version de la
WebBrowser
console basée sur le web scrapper peut être trouvé sur Github.Mis à jour: L'ajout d'une piscine de
Navigateur
objets pour plusieurs téléchargements en parallèle.Ci-dessous est une mise en œuvre plus ou moins générique
WebBrowser
web basé sur le scrapper, qui fonctionne comme application console. C'est un regroupement de certaines de mes précédentesWebBrowser
efforts liés, y compris le code référencé dans la question:La capture d'une image de la page web avec l'opacité
Le chargement d'une page dynamique avec AJAX le contenu
La création d'un STA message de la boucle de fil pour
Navigateur
Chargement d'un ensemble d'Url, l'un après l'autre
L'impression d'un ensemble d'Url avec
Navigateur
Page Web UI automation
Quelques points:
Réutilisables
MessageLoopApartment
classe est utilisé pour démarrer et exécuter un WinForms thread STA avec sa propre pompe de message. Il peut être utilisé à partir d'un application console, comme ci-dessous. Cette classe expose un TPL Planificateur de Tâches (FromCurrentSynchronizationContext
) et un ensemble deTask.Factory.StartNew
wrappers pour utiliser cette planificateur de tâches.Ce fait
async/await
un excellent outil pour l'exécution deWebBrowser
des tâches de navigation sur qui séparent thread STA. De cette façon, unWebBrowser
objet est créé, navigué et détruit sur ce thread. Bien que,MessageLoopApartment
n'est pas lié àWebBrowser
spécifiquement.Il est important d'activer HTML5 rendu à l'aide des Fonctionnalité Du Navigateur
Contrôle, sinon la
WebBrowser
obejcts s'exécute dans IE7 en mode d'émulation par défaut.C'est ce que
SetFeatureBrowserEmulation
ne ci-dessous.Il n'est pas toujours possible de déterminer quand une page web a fini de rendre avec 100% de probabilité. Certaines pages sont assez complexes et en continu à l'AJAX mises à jour. Pourtant, nous
pouvez obtenir assez proche, par la manipulation
DocumentCompleted
événement d'abord, puis l'interrogation de la page HTML en cours de capture instantanée pour des changements et de la vérification de laWebBrowser.IsBusy
de la propriété. C'est ce queNavigateAsync
ne ci-dessous.Un temps logique est présente sur le dessus de ce qui précède, dans le cas où le rendu de la page est sans fin (note
CancellationTokenSource
etCreateLinkedTokenSource
).WebBrowser
des cas, à un chiffre raisonnable, comme 3-4. Vous pouvez utiliserSemaphoreSlim.WaitAsync
pour cela (beaucoup d'exemples d'utilisation ici DONC). Une autre chose à garder à l'esprit, tous lesWebBrowser
instances partagent la même session HTTP (y compris les témoins).webBrowser.NavigateAsync
est terminé.Task.Run
serait un grand ajustement pour que. Autrement, bloquer un thread alors quewebBrowser.NavigateAsync
est "en vol" est une mauvaise idée. Si vous êtes intéressé, postez une question distincte, et je vais vous montrer ce que je veux dire avec le code, si le temps le permet.WebBrowser
à base de robot avec un certain degré de parallélisme. Inclure un lien vers cette question et l'étiqueter avec [webbrowser-control]. Je vais l'obtenir 🙂Delphi
depuis des siècles maintenant. Avec une coutume multithread backend pour chaque navigateur. De sorte qu'ils sont isolés les uns des autres (Cookies, Proxy) tout le toutim! 🙂IOCP
donc, il est très très efficace.WebBrowser
téléchargements utilisez IOCP, sans bloquer les OS des threads n'est pas un 100% fait, à ma connaissance. Je serais intéressé de savoir si vous pouvez prouver que, mais ce n'est pas le point. Le point est, si vous commencez à 400 téléchargements en parallèle et en entrant votre vitesse de connexion à internet est de 10 mbits / s, chaque téléchargement sera ramper à 2,5 Kbbps, qui est un modem d'accès à distance de la vitesse.URL: http://example.com
. Qu'est-ce WinForms causes d'un problème? Différences: j'ai créé une nouvelle classe de votre code-Program2
. J'ai ajouté un bouton d'un formulaire, et le bouton appelsProgram2.Start(new string[1]);
.Start
dans ma classe est ce qui remplace leMain
dans le vôtre. J'ai aussi essayé une autre version où j'ai utiliser la valeur par défautpublic partial class Form1 : Form
, le remplacement de votreMain
avec unbutton1_Click
qui contient le corps de votreMain
. Pas de chance. Des idées?Je soupçonne l'exécution d'une boucle de traitement sur un autre thread va pas bien, depuis
WebBrowser
est un composant de l'INTERFACE utilisateur qui héberge un contrôle ActiveX.Lorsque vous écrivez APPUYEZ sur EAP wrappers, je recommande d'utiliser des méthodes d'extension pour avoir un code propre:
Maintenant votre code peut facilement appliquer un délai d'attente:
qui peut être consommé en tant que tel:
Je suis en train de prendre avantage de Noseratio de la solution ainsi que de suivre les conseils de Stephen Cleary.
Voici le code que j'ai mis à jour pour inclure dans le code de Stephen le code de Noseratio concernant l'AJAX pointe.
Première partie: le
Task NavigateAsync
conseillé par StephenDeuxième partie: un nouveau
Task NavAjaxAsync
à la pointe de l'AJAX (basé sur Noseratio du code)Troisième partie: un nouveau
Task NavAndAjaxAsync
pour obtenir de la navigation et de l'AJAXQuatrième et dernière partie: la mise à jour
Task GetUrlAsync
de Stephen avec Noseratio du code AJAXJe voudrais savoir si c'est la bonne approche.