WebClient в потоках (C#)

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Almighty, 10 Jul 2010.

  1. Almighty

    Almighty New Member

    Joined:
    5 May 2010
    Messages:
    2
    Likes Received:
    0
    Reputations:
    0
    Здравствуйте.

    Учу C#.

    Надо:
    1. загрузить веб-страницу (делаю через WebBrowser)
    2. вытащить из html-кода адреса всех изображений (беру через Document.Images, ложу в HtmlElementCollection и выдергиваю адреса через GetAttribute("src") в foreach)
    3. загрузить все изображения на локальный компьютер, запустив загрузку каждого в отдельном потоке

    Проблемы с 3 пунктом. На странице 5 изображений, но они не грузятся, в нужной папке образуется 2-4 файла, иногда пустых (5 ни разу не было), иногда (не всегда) вылезает сообщение об ошибке COM:

    Code:
    An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in System.Windows.Forms.dll
     
    Additional information: COM object that has been separated from its underlying RCW cannot be used.
    Изначально в коде было DownloadFileAsync, после замены на просто DownloadFile опять стала нерегулярно вылетать ошибка COM, также теперь прут ошибки о невозможности доступа к файлу (файл занят другим процессом), но не для всех файлов и потоков такое, а только для 2-3 (из 5). Остальные файлы скачиваются нормально или пустые.

    Код:
    Code:
            static void Main()
             
            {
                Thread.CurrentThread.Name = "main_thread";
                
                try
                {
                    thrcnt = 0;
                    imgwork();
                }
                catch (Exception ex)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Error: {0}",ex);
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.ReadLine();
                };
            }
            
            static HtmlElementCollection loadwork()
            {
    		//тут первые 2 пункта, выполняются нормально
            }
    
            static void imgwork()
            {
                 HtmlElementCollection allimg = loadwork();
                foreach (HtmlElement cimg in allimg)
                {
                    Random rndm = new Random();
    
                            try
                            {
    
                                    Thread thr = new Thread(delegate() { loadimg(cimg.GetAttribute("src"), string.Concat("C:\\csh\\", rndm.Next(0, 1024).ToString(), ".gif")); });
    
                                    thrcnt++;
                                    thr.Name = thrcnt.ToString();
    
                                    Console.WriteLine("Starting thread {0}...", thr.Name.ToString());
                                    thr.Start();
    
                                    if (thr.ThreadState == ThreadState.Running)
                                        Console.WriteLine("Thread {0} started!", thr.Name.ToString());
                                    else
                                        throw new Exception("Thread error!");
                                    thr = null;
                            
    
    
                            Console.WriteLine("Loading {0}...", cimg.GetAttribute("src"));
                            
                            
                            }
                            catch (System.Runtime.InteropServices.InvalidComObjectException comex)
                            {
                                Console.WriteLine(comex);
                            }
            	}
    	}
    
            static void loadimg(string img_url, string save_path)
            {
    
    
    
                        Console.WriteLine("Thread {1}: Downloading {0}...", img_url, Thread.CurrentThread.Name.ToString());
                        WebClient wcl = new WebClient();
                        Uri url = new Uri(img_url);
    
                        try
                        {
    
                            wcl.DownloadFile(url, save_path);
    
                        }
                        catch (System.Net.WebException wex)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("Error: {0}", wex);
    
                            
                        }
                        Console.ForegroundColor = ConsoleColor.White;
    
                        Console.WriteLine("{0}: wcl.DownloadFile begin...", Thread.CurrentThread.Name.ToString());
    
    
                        wcl.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(wcl_DownloadFileCompleted);
    
    
            }
    
    lock пробовал ставить буквально на всё, не помогает.
    Прошу помощи, бьюсь уже третий день.
     
  2. BrainDeaD

    BrainDeaD Elder - Старейшина

    Joined:
    9 Jun 2005
    Messages:
    774
    Likes Received:
    292
    Reputations:
    214
    первую ошибку вижу в рандомных именах. дело в том, что числа могут повторятся, при этом возникает конфликт.
     
  3. gudus

    gudus New Member

    Joined:
    12 Jan 2008
    Messages:
    3
    Likes Received:
    1
    Reputations:
    0
    Вот мой пример может тебе поможет!

    День добрый, если ещё нужно, попробуй вот этот код, ему конечно далеко до совершенства, но... у меня просто задача практически идентична твоей...и у меня это работает, давольно не плохо...​

    Code:
    		static int CountThread = Program.countThread;
            Thread[] t = new Thread[CountThread];
            Thread Control;
            public int timerwhile = 0;
            static object locker = new object();
            delegate void SetTextCallback(string text);
    
            public int countB = 0;
            public int state = 0;
    
    		//для доступа к элементам из потока!!!
            private void SetText(string text)
            {
                    if (this.label5.InvokeRequired)
                    {
                        SetTextCallback d = new SetTextCallback(SetText);
                        this.Invoke(d, new object[] { text });
                        label5.Text = text;
                    }
                    else
                    {
                        label5.Text = text;
                    }
            }
    		//******************************************
            private void button1_Click(object sender, EventArgs e)
            {
                progressBar1.Maximum = getFullCountShpiWithNull();
                label4.Text = progressBar1.Maximum.ToString();
                timer1.Enabled = true;
                Control = new Thread(Start);
                Control.Start();
            }
    
            private void Start()
            {
                while (timerwhile == 0)
                {
                    for (int i = 0; i < CountThread; i++)
                    {
                        if (t[i] == null)
                        {
                            t[i] = new Thread(Start1);
                            t[i].Name = "Im-" + i.ToString();
                            t[i].Start();
                        }
                        else
                        {
                            if (t[i].ThreadState == ThreadState.Aborted)
                            {
                                t[i].Abort();
                                t[i] = new Thread(Start1);
                                t[i].Name = "Im-" + i.ToString();
                                t[i].Start();
                            }
                            if( (t[i].ThreadState == ThreadState.Stopped))
                            {
                                t[i] = new Thread(Start1);
                                t[i].Name = "Im-" + i.ToString();
                                t[i].Start();
                            }
                        }
                    }
                }
            }
    
            private void Start1()
            {
             //   Thread.CurrentThread.Join(1000);
                lock (locker)
                {
                    getState(getShpiFromBase());
                }
            //    Thread.CurrentThread.Join(1000);
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                for (int i = 0; i < CountThread; i++)
                {
                    if (t[i] != null)
                    {
                        t[i].Abort();
                    }
                }
                if (Control != null)
                {
                    Control.Abort();
                }
            }
    
            private void getState(object shpi)
            {
                string[] barcode = (string[])shpi;
                string str = "";
                string st1 = "";
                string st2 = "";
                int a, b, c;
                for (int d = 0; d < barcode.Length; d++)
                {
                    SetText((d + 1).ToString());
                    SetText2(d + 1);
                    try
                    {
                        if ((barcode[d] != null) && (barcode[d] != ""))
                        {
                            WebRequest request = HttpWebRequest.Create(Program.strWebReq + barcode[d]);
                            /*   if (Program.webpr.Address.AbsolutePath != "")
                               {
                                   request.Proxy = Program.webpr;
                               }*/
    
                            /* if (webproxy.UseProxy().Address.AbsoluteUri != "")
                            {
                                request.Proxy = webproxy.UseProxy();
                            }*/
                            request.Method = "POST";
                            request.Timeout = 1000*Program.TimeReqWeb;
    
                            WebResponse response = request.GetResponse();
                            StreamReader streamReader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(1251));
    
                    }
                    catch (Exception ex)
                    {
                        SaveLogs.SaveLogInFile(DateTime.Now.ToString() + "     " + ex.ToString() + "\r\n");
                    }
    
                    //Exit
                }
                countB = 0;
                a = progressBar1.Maximum;
                b = getFullCountShpiWithNull();
                c = a - b;
                SetText3(c);
                SetText2(0);
            }
    
     
  4. AlexTheC0d3r

    AlexTheC0d3r Elder - Старейшина

    Joined:
    25 Jul 2008
    Messages:
    388
    Likes Received:
    179
    Reputations:
    18
    Тоже хотел посоветовать использовать WebRequest и дергать картинки RegExp;
    gudus, зачем HttpWebRequest?
     
  5. gudus

    gudus New Member

    Joined:
    12 Jan 2008
    Messages:
    3
    Likes Received:
    1
    Reputations:
    0
    Просто я так привык..., у меня задача другая... да и я просто хотел показать как я реализовал вопрос с потоками...у меня работает на ура!!! вот только я чего-то не понимаю, люди тут пишут что хотят в 50-т потоков программы запускать,,,,, , насколько я слышал, 2-а потока на одно ядро....)) не более...а иначе смысла нет... очередь..
    вопрос: а имеет ли смысл делать в программе 50-т потоков если у меня 4-е ядра..??=))
     
  6. GlooK

    GlooK Elder - Старейшина

    Joined:
    20 Apr 2007
    Messages:
    172
    Likes Received:
    53
    Reputations:
    10
    Лучше использовать асинхронные запросы, нежели чем прямой запуск потоков.
     
  7. AlexTheC0d3r

    AlexTheC0d3r Elder - Старейшина

    Joined:
    25 Jul 2008
    Messages:
    388
    Likes Received:
    179
    Reputations:
    18

    Есть смысл, если тебе надо выполнить некоторый блок, в котором предусматривается задержка, и он циклический.

    Core i7 топовый поддерживает 12 потоков (2 потока на каждое из 6ти ядер) одновременно выполняющихся
     
  8. Kairos

    Kairos Elder - Старейшина

    Joined:
    5 Oct 2009
    Messages:
    37
    Likes Received:
    21
    Reputations:
    21
    Имеет. У тебя при скачивании поток останавливается и ждет данные, при этом освобождая процессор для остальных.
     
  9. gudus

    gudus New Member

    Joined:
    12 Jan 2008
    Messages:
    3
    Likes Received:
    1
    Reputations:
    0
    А в чём разница??? я просто не много не пойму??? у меня также получается... запускаю потоки, и они выполняю асинхронно запросы, единственное ограничение железа и канала, по мимо этого есть есть основной и контролирующий, чтобы не дать разрастись...и вроде как на тестах красоту показывает...,!
     
  10. GlooK

    GlooK Elder - Старейшина

    Joined:
    20 Apr 2007
    Messages:
    172
    Likes Received:
    53
    Reputations:
    10
    Я сам только начал программировать.
    На RSDN попросил прокритиковать код и мне посоветовали либо использовать асинхронные запросы, либо пул потоков. В общем это один из паттернов.
     
  11. Kairos

    Kairos Elder - Старейшина

    Joined:
    5 Oct 2009
    Messages:
    37
    Likes Received:
    21
    Reputations:
    21
    Помни, об этом стоит начинать беспокоиться если при работе твоего софта проц загружен на 100%, не раньше :)
    Не надо что-то чинить если оно и так работает.