【发布时间】:2014-04-09 23:12:09
【问题描述】:
我正在使用此方法以编程方式实例化 Web 浏览器,导航到 url 并在文档完成后返回结果。
如果文档加载时间超过 5 秒,我如何能够停止 Task 并让 GetFinalUrl() 返回 null?
我见过很多使用 TaskFactory 的示例,但我无法将它应用到这段代码中。
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
}
【问题讨论】:
-
很高兴看到有人实际上在使用this code :) 我还有另一个例子,它用超时做类似的事情:stackoverflow.com/a/21152965/1768303。寻找 var
cts = new CancellationTokenSource(30000)。 -
谢谢。您是否有任何机会在控制台应用程序中执行此操作的示例?另外我不认为 webBrowser 可以是一个类变量,因为我正在为每个并行运行整个事情,迭代数千个 URL
-
我使用了您在我的控制台应用程序中建议的代码并得到: System.Threading.ThreadStateException: ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantized because current thread is not in一个单线程的公寓。我猜这是消息循环工作线程在您的其他代码示例中所做的。这是我无法使用cancellationToken的原因。帮助表示赞赏。我会继续努力的。
-
看来它不仅需要在 STA 线程上运行,还需要一个消息循环工作者,如:stackoverflow.com/a/19737374/1768303
标签: c# .net webbrowser-control task-parallel-library console-application