【问题标题】:how to add queue tasks to threadpool in C#如何在 C# 中将队列任务添加到线程池
【发布时间】:2011-07-18 23:39:14
【问题描述】:

我对这个线程的东西还是个新手。假设我有 50000 个 URL,我想同时获取这些 URL 的内容,比如每 10 个 URL 一起处理。然后一旦这些 URL 之一完成处理,程序应该从队列列表中添加另一个 1,直到它完成处理列表中的所有 URL。现在我怎么能用 C# 做到这一点.. 这是我目前正在做的代码..

 class RequestState
        {
            public WebRequest Request;

        // holds the request 
        public object Data;

        // store any data in this 
        public string SiteUrl;

        // holds the UrlString to match up results (Database lookup, etc). 

        public RequestState(WebRequest request, object data, string siteUrl)
        {
            this.Request = request;
            this.Data = data;
            this.SiteUrl = siteUrl;
        }
    }

    private void PROCESS_URLS_Click(object sender, EventArgs e)
    {
        //run the process
        process_URLs();
    }

private int ThreadsCount = 0;

  private void process_URLs()
    {
       //count threads number
        ThreadsCount = URLS_LISTVIEW.SelectedItems.Count;

       //loop through all URLs in listview
        for (int i = 0; i < URLS_LISTVIEW.SelectedItems.Count; i++)
        {
            try
            {
                //get url string
                string myURLs = URLS_LISTVIEW.SelectedItems[i].SubItems[0].Text.Trim();

                // for each URL in the collection...
                WebRequest request = HttpWebRequest.Create(myURLs);
                request.Method = "GET";
                object data = new object();

                RequestState state = new RequestState(request, data, myURLs);
                IAsyncResult result = request.BeginGetResponse(new AsyncCallback(UpdateItem), state);
                ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(ScanTimeoutCallback), state, (30 * 1000), true);

            }
            catch (ThreadStateException es)
            {
                MessageBox.Show(es.Message);
            }

        }



    }




 private void UpdateItem(IAsyncResult result)
    {
        RequestState state = (RequestState)result.AsyncState;
        WebRequest request = (WebRequest)state.Request;
        try
        {// grab the custom state object
            // get the Response
            HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);

            // process the response...
            Stream s = (Stream)response.GetResponseStream();
            StreamReader readStream = new StreamReader(s);

            //data grabbed
            string dataString = readStream.ReadToEnd();
            response.Close();
            s.Close();
            readStream.Close();



        //finished grabbing content for this thread.
        ThreadsCount = ThreadsCount - 1;


        //if all threads finished running then execute final code to tell the user the process finished
        if (ThreadsCount < 1)
        {
            //show message
            MessageBox.Show("finished");
        }

       // Thread.Sleep(400);

    }





private static void ScanTimeoutCallback(object state, bool timedOut)
    {
        if (timedOut)
        {
            RequestState reqState = (RequestState)state;

            if (reqState != null)
                reqState.Request.Abort();


        }
    }

任何想法都将不胜感激:)

亲切的问候,

【问题讨论】:

    标签: c# queue threadpool add task


    【解决方案1】:

    看看 TPL,有一个选项可以指定最大并行度:

    List<string> UriList = new List<string>();
    ...
    Parallel.ForEach(UriList, 
                     new ParallelOptions() {MaxDegreeOfParallelism=10}, 
                     (x) =>
    {
        ProcessUrl(x);
    });
    

    这将最多并行处理 10 个 Url,因为我们使用 Parallel.Foreach() 的重载允许我们指定 MaxDegreeOfParallelism

    编辑:

    这里有一个简单的例子,它从http://google.com 并行下载 Html 50 次(但最多同时有 10 个线程)并将结果存储在一个数组中:

    List<string> UriList = new List<string>();
    for(int i =0;i<50;i++)
        UriList.Add("http://google.com");
    
    string[] HtmlResults = new string[UriList.Count];
    
    Parallel.ForEach(UriList, 
                     new ParallelOptions() { MaxDegreeOfParallelism = 10 }, 
                     (url, i, j) =>
    {
        WebClient wc = new WebClient();
        HtmlResults[j] = wc.DownloadString(url);
    });
    

    不要造成更多混乱,但在您的特定情况下,PLINQ 也可以很好地工作,因为要处理的项目之间没有依赖关系,并且您的实际结果是 URL 被“转换”为:

    var htmlResultList = UriList.AsParallel()
                                .WithDegreeOfParallelism(10)
                                .AsOrdered()
                                .Select(url => { WebClient wc = new WebClient(); return wc.DownloadString(url); })
                                .ToList();
    

    【讨论】:

    • 这意味着我需要从头开始写整个东西?
    • 您仍然可以重用实际处理 Url 的代码,例如WebRequest 等 - 但我个人什至会通过使用WebClient 来简化它.不过考虑一下长期利益 - 需要维护的代码要少得多。
    • @ermac2014:更新了一个例子
    • 感谢 :).. 但是现在当我运行代码时,整个 UI 会冻结,直到它完成从 50 个 URL 中抓取所有内容有什么想法吗?
    • @ermac2014:Parallel.Foreach() 语句只有在所有内容下载完成后才会结束——它是同步的。如果您希望它是异步的,则必须生成一个线程来运行它,即将它包装在 Task 中。
    【解决方案2】:

    (这应该是@BrokenGlass下的评论,但我还不能发布cmets)

    您可以查看this article,了解如何使用并行处理和 PLINQ 来完成您的任务。前面的整套文章也有一些很好的信息。

    编辑:如果这是独立的,请生成一个新线程以在后台运行此部分,以免导致 UI 无响应。

    编辑 2:如果需要,您还可以将字符串放入 ConcurrentQueue,以便在查找项目时从 UI 添加项目。

    【讨论】:

    • 您的意思是我需要使用新线程来执行此操作或使用后台工作人员?或者没关系两者都会做同样的事情..?
    • @ermac2014 - 你可以做任何一个。只需将工作推到与您的 UI 无关的其他线程即可防止它挂起。然后,当工作完成后,只需通过一些回调将结果更新到 UI。
    • 现在说得通了 :) 如果我需要进一步的帮助,我会告诉你的。谢谢你的提示非常感谢..
    • 别担心!很高兴我能提供帮助。
    猜你喜欢
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 2019-09-13
    • 2010-12-18
    • 2011-01-08
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    相关资源
    最近更新 更多