C# 如何在超时后取消任务等待

C# 如何在超时后取消任务等待,c#,.net,webbrowser-control,task-parallel-library,console-application,C#,.net,Webbrowser Control,Task Parallel Library,Console Application,我使用此方法以编程方式实例化web浏览器,导航到url,并在文档完成后返回结果 如果加载文档的时间超过5秒,我如何停止任务并让GetFinalUrl()返回null 我见过许多使用TaskFactory的示例,但我无法将其应用于此代码 private Uri GetFinalUrl(PortalMerchant portalMerchant) { SetBrowserFeatureControl(); Uri finalUri = null;

我使用此方法以编程方式实例化web浏览器,导航到url,并在文档完成后返回结果

如果加载文档的时间超过5秒,我如何停止
任务
并让
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
}
private Uri GetFinalUrl(PortalMerchant PortalMerchant)
{
SetBrowserFeatureControl();
Uri finalUri=null;
if(string.IsNullOrEmpty(portalMethant.Url))
{
返回null;
}
uritrackingurl=新的Uri(portalMerchant.Url);
var task=MessageLoopWorker.Run(DoWorkAsync,trackingUrl);
task.Wait();
如果(!String.IsNullOrEmpty(task.Result.ToString()))
{
返回新Uri(task.Result.ToString());
}
其他的
{
抛出新异常(“解析失败”);
}
}
//按比例—http://stackoverflow.com/users/1768303/noseratio    
静态异步任务DoWorkAsync(对象[]args)
{
_threadCount++;
Console.WriteLine(“线程计数:”+_线程计数);
uriretval=null;
var wb=新的WebBrowser();
wb.ScriptErrorsSuppressed=true;
TaskCompletionSource tcs=null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler=(s,e)=>tcs.TrySetResult(true);
foreach(参数中的变量url)
{
tcs=新任务完成源();
wb.DocumentCompleted+=documentCompletedHandler;
尝试
{
导航(url.ToString());
等待tcs任务;
}
最后
{
wb.DocumentCompleted-=documentCompletedHandler;
}
retVal=wb.Url;
wb.Dispose();
返回返回;
}
返回null;
}
公共静态类MessageLoopWorker
{
#区域公共静态方法
公共静态异步任务运行(Func worker,params object[]args)
{
var tcs=new TaskCompletionSource();
变量线程=新线程(()=>
{
EventHandler idleHandler=null;
idleHandler=async(s,e)=>
{
//处理应用程序。仅空闲一次
Application.Idle-=idleHandler;
//返回到消息循环
等待任务;
//并异步继续
//提出结果或例外
尝试
{
var结果=等待工作人员(args);
tcs.SetResult(结果);
}
捕获(例外情况除外)
{
tcs.SetException(ex);
}
//退出消息循环的信号
//此时将退出Application.Run
Application.ExitThread();
};
//处理应用程序。仅空闲一次
//确保我们在消息循环中
//和SynchronizationContext已正确安装
Application.Idle+=idleHandler;
Application.Run();
});
//为新线程设置STA模型
SetApartmentState(ApartmentState.STA);
//启动线程并等待任务
thread.Start();
尝试
{
返回等待任务;
}
最后
{
thread.Join();
}
}
#端区
}

我怀疑在另一个线程上运行处理循环不会很好,因为
WebBrowser
是承载ActiveX控件的UI组件

在编写时,我建议您使用扩展方法保持代码整洁:

public static Task<string> NavigateAsync(this WebBrowser @this, string url)
{
  var tcs = new TaskCompletionSource<string>();
  WebBrowserDocumentCompletedEventHandler subscription = null;
  subscription = (_, args) =>
  {
    @this.DocumentCompleted -= subscription;
    tcs.TrySetResult(args.Url.ToString());
  };
  @this.DocumentCompleted += subscription;
  @this.Navigate(url);
  return tcs.Task;
}
公共静态任务NavigateAsync(此WebBrowser@this,字符串url)
{
var tcs=new TaskCompletionSource();
WebBrowserDocumentCompletedEventHandler订阅=null;
订阅=(\ux,args)=>
{
@this.DocumentCompleted-=订阅;
TrySetResult(args.Url.ToString());
};
@this.DocumentCompleted+=订阅;
@这个.导航(url);
返回tcs.Task;
}
现在,您的代码可以轻松应用超时:

async Task<string> GetUrlAsync(string url)
{
  using (var wb = new WebBrowser())
  {
    var navigate = wb.NavigateAsync(url);
    var timeout = Task.Delay(TimeSpan.FromSeconds(5));
    var completed = await Task.WhenAny(navigate, timeout);
    if (completed == navigate)
      return await navigate;
    return null;
  }
}
异步任务GetUrlAsync(字符串url) { 使用(var wb=new WebBrowser()) { var navigate=wb.NavigateAsync(url); var timeout=任务延迟(TimeSpan.FromSeconds(5)); var completed=等待任务.wheny(导航,超时); 如果(已完成==导航) 返回等待导航; 返回null; } } 可按如下方式使用:

private async Task<Uri> GetFinalUrlAsync(PortalMerchant portalMerchant)
{
  SetBrowserFeatureControl();
  if (string.IsNullOrEmpty(portalMerchant.Url))
    return null;
  var result = await GetUrlAsync(portalMerchant.Url);
  if (!String.IsNullOrEmpty(result))
    return new Uri(result);
  throw new Exception("Parsing Failed");
}
private异步任务GetFinalUrlAsync(PortalMethant PortalMethant)
{
SetBrowserFeatureControl();
if(string.IsNullOrEmpty(portalMethant.Url))
返回null;
var result=await GetUrlAsync(portalMerchant.Url);
如果(!String.IsNullOrEmpty(结果))
返回新的Uri(结果);
抛出新异常(“解析失败”);
}

更新:基于
WebBrowser
的控制台web刮板的最新版本可以下载

已更新:用于多个并行下载

你有没有关于如何在控制台应用程序中通过任何 机会?我也不认为webBrowser是一个类变量,因为 我以并行方式运行整个过程,每次迭代 数千个网址

下面是一个或多或少通用的**
WebBrowser
-基于web的scraper**的实现,它作为控制台应用程序工作。这是对我以前的一些
WebBrowser
相关工作的整合,包括问题中引用的代码:

有几点:

  • 可重用的
    messageloopfamilt
    类用于启动和运行带有自己消息泵的WinForms STA线程。信息技术
    public static Task<string> NavigateAsync(this WebBrowser @this, string url)
    {
      var tcs = new TaskCompletionSource<string>();
      WebBrowserDocumentCompletedEventHandler subscription = null;
      subscription = (_, args) =>
      {
        @this.DocumentCompleted -= subscription;
        tcs.TrySetResult(args.Url.ToString());
      };
      @this.DocumentCompleted += subscription;
      @this.Navigate(url);
      return tcs.Task;
    }
    
    public static async Task<string> NavAjaxAsync(this WebBrowser @this)
    {
      // get the root element
      var documentElement = @this.Document.GetElementsByTagName("html")[0];
    
      // poll the current HTML for changes asynchronosly
      var html = documentElement.OuterHtml;
    
      while (true)
      {
        // wait asynchronously
        await Task.Delay(POLL_DELAY);
    
        // continue polling if the WebBrowser is still busy
        if (webBrowser.IsBusy)
          continue;
    
        var htmlNow = documentElement.OuterHtml;
        if (html == htmlNow)
          break; // no changes detected, end the poll loop
    
        html = htmlNow;
      }
    
      return @this.Document.Url.ToString();
    }
    
    public static async Task NavAndAjaxAsync(this WebBrowser @this, string url)
    {
      await @this.NavigateAsync(url);
      await @this.NavAjaxAsync();
    }
    
    async Task<string> GetUrlAsync(string url)
    {
      using (var wb = new WebBrowser())
      {
        var navigate = wb.NavAndAjaxAsync(url);
        var timeout = Task.Delay(TimeSpan.FromSeconds(5));
        var completed = await Task.WhenAny(navigate, timeout);
        if (completed == navigate)
          return await navigate;
        return null;
      }
    }