C# 您是否需要后台工作线程或多个线程来触发多个异步HttpWebRequests? 总体目标

C# 您是否需要后台工作线程或多个线程来触发多个异步HttpWebRequests? 总体目标,c#,multithreading,asynchronous,httpwebrequest,backgroundworker,C#,Multithreading,Asynchronous,Httpwebrequest,Backgroundworker,我试图通过从.txt文件读取的多输入URL调用,并将结果输出到.csv 我试过的 我编写了一个控制台应用程序来尝试触发这些请求,然后当它们返回并添加到列表中时,当它们全部完成时,将列表写入.csv文件(async在尝试立即将响应写入.csv时有点疯狂) 我的代码如下所示,远未优化。我来自JavaScript背景,通常不使用web workers或任何其他托管新线程,因此我尝试在C#中使用同样的方法 我可以运行多个WebRequests并将它们写入一个集合(或输出文件),而不使用多个线程,并让它们

我试图通过从
.txt
文件读取的多输入URL调用,并将结果输出到
.csv

我试过的 我编写了一个控制台应用程序来尝试触发这些请求,然后当它们返回并添加到列表中时,当它们全部完成时,将
列表
写入
.csv
文件(async在尝试立即将响应写入
.csv
时有点疯狂)

我的代码如下所示,远未优化。我来自JavaScript背景,通常不使用web workers或任何其他托管新线程,因此我尝试在C#中使用同样的方法

  • 我可以运行多个
    WebRequest
    s并将它们写入一个集合(或输出文件),而不使用多个线程,并让它们并行运行,而不必等待每个请求返回,然后再处理下一个请求吗
  • 有没有更干净的方法来处理回调
  • 如果需要线程或
    BackgroundWorker
    s,有什么办法
  • 初始示例代码
    static void Main(字符串[]args)
    {
    WriteLine(“开始谷歌PageSpeed洞察!”);
    appMode=ConfigurationManager.AppSettings[“ApplicationMode”];
    var inputFilePath=READ_WRITE_PATH+ConfigurationManager.AppSettings[“InputFile”];
    var outputFilePath=READ_WRITE_PATH+ConfigurationManager.AppSettings[“OutputFile”];
    var inputLines=File.ReadAllLines(inputFilePath.ToList();
    if(File.Exists(outputFilePath))
    {
    File.Delete(outputFilePath);
    }
    List outputCache=新列表();
    foreach(输入行中的var行)
    {
    var requestDataFromPsi=CallPsiForPrimaryStats(行);
    WriteLine($“获取了{requestDataFromPsi.Result}的响应”);
    Add(requestDataFromPsi.Result);
    }
    var writeTask=WriteCharacters(outputCache,outputFilePath);
    writeTask.Wait();
    Console.WriteLine(“结束Google PageSpeed Insights”);
    }
    私有静态异步任务CallPsiForPrimaryStats(字符串url)
    {
    HttpWebRequest myReq=(HttpWebRequest)WebRequest.Create($)https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url={url}&strategy=mobile&key={API_key}”);
    myReq.Method=WebRequestMethods.Http.Get;
    myReq.Timeout=60000;
    myReq.Proxy=null;
    myReq.ContentType=“应用程序/json”;
    Task Task=Task.Factory.fromsync(
    myReq.BeginGetResponse,
    asyncResult=>myReq.EndGetResponse(asyncResult),
    (对象为空);
    返回等待任务。继续(t=>ReadStreamFromResponse(t.Result));
    }
    私有静态字符串ReadStreamFromResponse(WebResponse响应)
    {
    使用(Stream responseStream=response.GetResponseStream())
    使用(StreamReader sr=新StreamReader(responseStream))
    {
    字符串jsonResponse=sr.ReadToEnd();
    动态jsonObj=Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse);
    var psiResp=新的PsiResponse()
    {
    Url=jsonObj.id,
    SpeedScore=jsonObj.ruleGroups.SPEED.score,
    UsabilityScore=jsonObj.ruleGroups.USABILITY.score,
    NumberResources=jsonObj.pageStats.NumberResources,
    NumberHosts=jsonObj.pageStats.NumberHosts,
    TotalRequestBytes=jsonObj.pageStats.TotalRequestBytes,
    NumberStaticResources=jsonObj.pageStats.NumberStaticResources,
    HtmlResponseBytes=jsonObj.pageStats.HtmlResponseBytes,
    CssResponseBytes=jsonObj.pageStats.CssResponseBytes,
    ImageResponseBytes=jsonObj.pageStats.ImageResponseBytes,
    JavascriptResponseBytes=jsonObj.pageStats.JavascriptResponseBytes,
    OtherResponseBytes=jsonObj.pageStats.OtherResponseBytes,
    NumberJsResources=jsonObj.pageStats.NumberJsResources,
    numbercsresources=jsonObj.pageStats.numbercsresources,
    };
    返回CreateOutputString(PSIREP);
    }
    }
    静态异步任务WriteCharacters(列表输入、字符串输出文件路径)
    {
    使用(StreamWriter fileWriter=newstreamwriter(outputFilePath))
    {
    等待fileWriter.WriteLineAsync(表头);
    foreach(输入中的var输入)
    {
    等待fileWriter.WriteLineAsync(输入);
    }
    }
    }
    专用静态字符串CreateOutputString(PsiResponse PsiResponse)
    {
    var stringToWrite=“”;
    foreach(psiResponse.GetType().GetProperties()中的var prop)
    {
    stringToWrite+=$“{prop.GetValue(psiResponse,null)},”;
    }
    控制台写入线(stringToWrite);
    返回stringToWrite;
    }
    
    更新:Stephen Cleary Tips重构后 问题是这仍然运行缓慢。最初的需要20分钟,重构后仍然需要20分钟。它似乎在某个地方被限制了,可能是谷歌在PageSpeed API上限制的。我对它进行了测试、呼叫和其他18次测试,它运行速度也很慢,一次只能处理大约5个请求。我尝试重构运行5个测试URL,然后写入文件并重复,但这只是略微加快了过程

    static void Main(string[] args) { MainAsync(args).Wait(); }
    static async Task MainAsync(string[] args)
    {
        Console.WriteLine("Begin Google PageSpeed Insights!");
    
        appMode = ConfigurationManager.AppSettings["ApplicationMode"];
        var inputFilePath = READ_WRITE_PATH + ConfigurationManager.AppSettings["InputFile"];
        var outputFilePath = READ_WRITE_PATH + ConfigurationManager.AppSettings["OutputFile"];
    
        var inputLines = File.ReadAllLines(inputFilePath).ToList();
    
        if (File.Exists(outputFilePath))
        {
            File.Delete(outputFilePath);
        }
    
        var tasks = inputLines.Select(line => CallPsiForPrimaryStats(line));
        var outputCache = await Task.WhenAll(tasks);
    
        await WriteCharacters(outputCache, outputFilePath);
    
        Console.WriteLine("End Google PageSpeed Insights");
    }
    
    private static async Task<string> CallPsiForPrimaryStats(string url)
    {
        HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create($"https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url={url}&strategy=mobile&key={API_KEY}");
        myReq.Method = WebRequestMethods.Http.Get;
        myReq.Timeout = 60000;
        myReq.Proxy = null;
        myReq.ContentType = "application/json";
        Console.WriteLine($"Start call: {url}");
    
        // Try using `HttpClient()` later
        //var myReq2 = new HttpClient();
        //await myReq2.GetAsync($"https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url={url}&strategy=mobile&key={API_KEY}");
    
        Task<WebResponse> task = Task.Factory.FromAsync(
            myReq.BeginGetResponse,
            myReq.EndGetResponse,
            (object)null);
        var result = await task;
        return ReadStreamFromResponse(result);
    }
    
    private static string ReadStreamFromResponse(WebResponse response)
    {
        using (Stream responseStream = response.GetResponseStream())
        using (StreamReader sr = new StreamReader(responseStream))
        {
            string jsonResponse = sr.ReadToEnd();
            dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse);
    
            var psiResp = new PsiResponse()
            {
                Url = jsonObj.id,
                SpeedScore = jsonObj.ruleGroups.SPEED.score,
                UsabilityScore = jsonObj.ruleGroups.USABILITY.score,
                NumberResources = jsonObj.pageStats.numberResources,
                NumberHosts = jsonObj.pageStats.numberHosts,
                TotalRequestBytes = jsonObj.pageStats.totalRequestBytes,
                NumberStaticResources = jsonObj.pageStats.numberStaticResources,
                HtmlResponseBytes = jsonObj.pageStats.htmlResponseBytes,
                CssResponseBytes = jsonObj.pageStats.cssResponseBytes,
                ImageResponseBytes = jsonObj.pageStats.imageResponseBytes,
                JavascriptResponseBytes = jsonObj.pageStats.javascriptResponseBytes,
                OtherResponseBytes = jsonObj.pageStats.otherResponseBytes,
                NumberJsResources = jsonObj.pageStats.numberJsResources,
                NumberCssResources = jsonObj.pageStats.numberCssResources,
    
            };
            return CreateOutputString(psiResp);
        }
    }
    
    static async Task WriteCharacters(IEnumerable<string> inputs, string outputFilePath)
    {
        using (StreamWriter fileWriter = new StreamWriter(outputFilePath))
        {
            await fileWriter.WriteLineAsync(TABLE_HEADER);
    
            foreach (var input in inputs)
            {
                await fileWriter.WriteLineAsync(input);
            }
        }
    }
    
    private static string CreateOutputString(PsiResponse psiResponse)
    {
        var stringToWrite = "";
        foreach (var prop in psiResponse.GetType().GetProperties())
        {
            stringToWrite += $"{prop.GetValue(psiResponse, null)},";
        }
        Console.WriteLine(stringToWrite);
        return stringToWrite;
    }
    
    static void Main(字符串[]args){mainsync(args.Wait();}
    静态异步任务mainsync(字符串[]args)
    {
    WriteLine(“开始谷歌PageSpeed洞察!”);
    appMode=ConfigurationManager.AppSettings[“ApplicationMode”];
    var inputFilePath=READ_WRITE_PATH+ConfigurationManager.AppSettings[“InputFile”];
    var outputFilePath=READ_WRITE_PATH+ConfigurationManager.AppSettings[“OutputFile”];
    var inputLines=File.ReadAllLines(inputFilePath.ToList();
    if(File.Exists(outputFilePath))
    {
    File.Delete(outputFilePath);
    }
    var tasks=inputLines.Select(line=>CallPsiForPrimaryStats(line));
    var outputCache=wait Task.WhenAll(任务);
    等待写入字符(outputCache、outputFilePath);
    Console.WriteLine(“结束Google PageSpeed Insights”);
    
    static void Main(string[] args) { MainAsync(args).Wait(); }
    static async Task MainAsync(string[] args)
    {
        Console.WriteLine("Begin Google PageSpeed Insights!");
    
        appMode = ConfigurationManager.AppSettings["ApplicationMode"];
        var inputFilePath = READ_WRITE_PATH + ConfigurationManager.AppSettings["InputFile"];
        var outputFilePath = READ_WRITE_PATH + ConfigurationManager.AppSettings["OutputFile"];
    
        var inputLines = File.ReadAllLines(inputFilePath).ToList();
    
        if (File.Exists(outputFilePath))
        {
            File.Delete(outputFilePath);
        }
    
        var tasks = inputLines.Select(line => CallPsiForPrimaryStats(line));
        var outputCache = await Task.WhenAll(tasks);
    
        await WriteCharacters(outputCache, outputFilePath);
    
        Console.WriteLine("End Google PageSpeed Insights");
    }
    
    private static async Task<string> CallPsiForPrimaryStats(string url)
    {
        HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create($"https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url={url}&strategy=mobile&key={API_KEY}");
        myReq.Method = WebRequestMethods.Http.Get;
        myReq.Timeout = 60000;
        myReq.Proxy = null;
        myReq.ContentType = "application/json";
        Console.WriteLine($"Start call: {url}");
    
        // Try using `HttpClient()` later
        //var myReq2 = new HttpClient();
        //await myReq2.GetAsync($"https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url={url}&strategy=mobile&key={API_KEY}");
    
        Task<WebResponse> task = Task.Factory.FromAsync(
            myReq.BeginGetResponse,
            myReq.EndGetResponse,
            (object)null);
        var result = await task;
        return ReadStreamFromResponse(result);
    }
    
    private static string ReadStreamFromResponse(WebResponse response)
    {
        using (Stream responseStream = response.GetResponseStream())
        using (StreamReader sr = new StreamReader(responseStream))
        {
            string jsonResponse = sr.ReadToEnd();
            dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse);
    
            var psiResp = new PsiResponse()
            {
                Url = jsonObj.id,
                SpeedScore = jsonObj.ruleGroups.SPEED.score,
                UsabilityScore = jsonObj.ruleGroups.USABILITY.score,
                NumberResources = jsonObj.pageStats.numberResources,
                NumberHosts = jsonObj.pageStats.numberHosts,
                TotalRequestBytes = jsonObj.pageStats.totalRequestBytes,
                NumberStaticResources = jsonObj.pageStats.numberStaticResources,
                HtmlResponseBytes = jsonObj.pageStats.htmlResponseBytes,
                CssResponseBytes = jsonObj.pageStats.cssResponseBytes,
                ImageResponseBytes = jsonObj.pageStats.imageResponseBytes,
                JavascriptResponseBytes = jsonObj.pageStats.javascriptResponseBytes,
                OtherResponseBytes = jsonObj.pageStats.otherResponseBytes,
                NumberJsResources = jsonObj.pageStats.numberJsResources,
                NumberCssResources = jsonObj.pageStats.numberCssResources,
    
            };
            return CreateOutputString(psiResp);
        }
    }
    
    static async Task WriteCharacters(IEnumerable<string> inputs, string outputFilePath)
    {
        using (StreamWriter fileWriter = new StreamWriter(outputFilePath))
        {
            await fileWriter.WriteLineAsync(TABLE_HEADER);
    
            foreach (var input in inputs)
            {
                await fileWriter.WriteLineAsync(input);
            }
        }
    }
    
    private static string CreateOutputString(PsiResponse psiResponse)
    {
        var stringToWrite = "";
        foreach (var prop in psiResponse.GetType().GetProperties())
        {
            stringToWrite += $"{prop.GetValue(psiResponse, null)},";
        }
        Console.WriteLine(stringToWrite);
        return stringToWrite;
    }
    
    var resultTask = Task.WhenAll(
        inputLines.Select(line => CallPsiForPrimaryStats(line)).ToArray());
    
    static void Main(string[] args) { MainAsync(args).Wait(); }
    static async Task MainAsync(string[] args)
    {
    
      ...
      var tasks = inputLines.Select(line => CallPsiForPrimaryStats(line));
      var outputCache = await Task.WhenAll(tasks);
      await WriteCharacters(outputCache, outputFilePath);
      ...
    
    private static async Task<string> CallPsiForPrimaryStats(string url)
    {
      ...
      Task<WebResponse> task = Task.Factory.FromAsync(
          myReq.BeginGetResponse,
          myReq.EndGetResponse,
          (object)null);
      var result = await task;
      return ReadStreamFromResponse(result);
    }