Темы не отвечают

Я пытаюсь решить эту проблему с 2-3 часов, но не могу ее исправить. Пытаюсь скачать файл в три части три потока. проблема в том, что когда одна часть завершена, другие темы перестают скачиваться.

Example: let's say i want to download 300kb 
part1->t1->100kb
part2->t2->100kb  //if this thread get completed then other two become unresponsive.
part3->t3->100kb     

Код, с которым я работаю (измененный и более короткий, но решает мою проблему)

//inside a function
            WebResponse wresp = wreq.GetResponse();
            long e1 = wresp.ContentLength / 3;
            long e2 = 2*e1;
            long e3 = wresp.ContentLength;
            wreq.Abort();
            wresp.Close();
            wreq = null;
            wresp = null;
            byte[] buff1 = new byte[1500];
            byte[] buff2 = new byte[1500];
            byte[] buff3 = new byte[1500];
            HttpWebRequest hr1 = (HttpWebRequest)WebRequest.Create(textBox1.Text);
            hr1.AddRange(0, e1-1);
            WebResponse wresp1 = hr1.GetResponse();
            HttpWebRequest hr2 = (HttpWebRequest)WebRequest.Create(textBox1.Text);
            hr2.AddRange(e1,e2-1);
            WebResponse wresp2 = hr2.GetResponse();
            HttpWebRequest hr3 = hr1;//(HttpWebRequest)WebRequest.Create(textBox1.Text);
            hr3.AddRange(e2,e3);
            WebResponse wresp3 = hr3.GetResponse();
            Stream response1 = wresp1.GetResponseStream();
            Stream  response2 = wresp2.GetResponseStream();
            Stream response3 = wresp3.GetResponseStream();
            Stream f1, f2, f3;
            f1 = File.Create("Part1");
            f2 = File.Create("Part2");
           f3 = File.Create("Part3");
            int bytesRead=0, bytesProcessed=0;
            long total1=e1, total2=e2-e1, total3=e3-e2;
            int x1=0, x2=0, x3=0;
            Thread t1 = new Thread(() =>download(hr1, wresp1, buff1,response1,f1,bytesRead, bytesProcessed,total1,x1));
            t1.Name = "1";
            Thread t2 = new Thread(() => download(hr2, wresp2, buff2, response2, f2, bytesRead, bytesProcessed,total2,x2));
            t2.Name = "2";
            Thread t3 = new Thread(() => download(hr3, wresp3, buff3, response3, f3, bytesRead, bytesProcessed, total3,x3));
            t3.Name = "3";
            t1.Start();
            t2.Start();
            t3.Start();
            }

        }
        }

    private download(HttpWebRequest hr2, WebResponse wresp2, byte[] buff, Stream response, Stream f,int bytesRead,long bytesProcessed,long total,int x)
    {
        do
        {
            lock (lockerObj)
            {
                bytesRead = response.Read(buff, 0, buff.Length);
                bytesProcessed += bytesRead;
                f.Write(buff, 0, bytesRead);
                x = Convert.ToInt32(Thread.CurrentThread.Name) - 1;
                pb[x].Invoke((Action)delegate
                {
                    pb[x].Value = Convert.ToInt32(bytesProcessed * 100 / total);

                });
                if (bytesProcessed >= total)
                {
                    Thread.CurrentThread.Abort();
                    break;
                }
                lb[x].Invoke((Action)delegate
                {
                    lb[x].Text = "Downloaded " + Convert.ToDouble((bytesProcessed * 100 / total)).ToString() + "%";
                    label4.Text = "thread" + (x + 1);
                });

            }
        } while (bytesProcessed<=total && bytesRead>=0 );
    }
    static readonly Object lockerObj=new Object(); //at global level

Пользовательский интерфейс приложения остается отзывчивым, и из отладки я понял, что каждый поток выходит в одно и то же время. Итак, вопрос в том, почему другие потоки не завершают свои части.

введите здесь описание изображения


person Arpit    schedule 05.11.2013    source источник
comment
BytesRead и BytesProcessed используются по ссылке между тремя потоками? что приводит к сбою вашего while() при загрузке после завершения 1 потока?   -  person Monkpit    schedule 06.11.2013
comment
@KylePittman Нет; он написан запутанно, но значения этих переменных копируются в хранилище параметров при вызове метода. Каждый поток имеет свою собственную копию переменной. Однако код очень вводит в заблуждение.   -  person Servy    schedule 06.11.2013
comment
@KylePittman, но я думаю, это простой вызов по значению ..   -  person Arpit    schedule 06.11.2013
comment
Вы действительно не должны блокировать всю важную работу; это в значительной степени противоречит цели создания нескольких потоков. Только один когда-либо делает что-либо, кроме того, что сидит и ничего не делает в любой данный момент времени. Как бы то ни было, у вас даже не может быть одного работающего, если другой зависает и блокируется при чтении.   -  person Servy    schedule 06.11.2013
comment
лучше попробовать это на другом сайте и быть уверенным в правилах безопасности вашего сервера.   -  person BRAHIM Kamel    schedule 06.11.2013
comment
Кроме того, вы пытаетесь загрузить одиночный файл. Это никак нельзя парализовать. Три потока читают из одного потока не быстрее, чем один поток; сеть является узким местом. И выполнение трех разных сетевых запросов, чтобы каждый из трех потоков мог прочитать треть данных, просто замедлит вас, поскольку узкое место (сетевое соединение) должно выполнять в три раза больше работы.   -  person Servy    schedule 06.11.2013
comment
@Servy на самом деле я получил эту идею от IDM .. так что, как вы думаете, простой webclient будет столь же или более эффективным при загрузке одного файла?   -  person Arpit    schedule 06.11.2013
comment
@Arpit Вам будет намного легче, это действительно сработает, и да, вероятно, будет работать лучше. (Что спорно, если ваше решение даже не работает. Производительность стоит сравнивать только тогда, когда оба решения работают.)   -  person Servy    schedule 06.11.2013
comment
попробуйте скачать файл с IDM, если это работает нет причин не работать с вашим собственным кодом   -  person BRAHIM Kamel    schedule 06.11.2013
comment
@BRAHIMKamel работает в IDM   -  person Arpit    schedule 06.11.2013
comment
@Servy Я использую веб-клиент для невозобновляемых загрузок. Я пробую эту версию для возобновляемых загрузок. Любая идея трюка с IDM?   -  person Arpit    schedule 06.11.2013


Ответы (2)


У вас есть это условие в конце цикла в вашем методе download:

while (bytesProcessed<=0 && bytesRead>0 );

Это твоя проблема. В первый раз в цикле bytesProcessed увеличивается на количество прочитанных байтов, что делает его больше 0, что приводит к тому, что условие становится ложным, и цикл завершается.

Вы никогда не сделаете более одной итерации в цикле.

Кстати, не каждый ответ будет иметь допустимый ContentLength. Веб-страницы обычно не содержат заголовка Content-Length, и в этом случае свойство ContentLength будет равно -1. Даже если страница содержит заголовок Content-Length, нет гарантии, что он правильный. Таким образом, в общем случае вы не можете гарантировать, что получили все содержимое.

Как указал Серви в своем комментарии, разделение загрузки между несколькими потоками вряд ли улучшит производительность и, скорее всего, может привести к замедлению загрузки. Это особенно верно, если сайт видит, что вы делаете три одновременных запроса, и решает ограничить или заблокировать вас. В общем, вам лучше сделать обычную загрузку с одним файлом HttpWebRequest. Это более надежно, упрощает ваш код и, вероятно, будет работать лучше, чем слишком сложная многопоточная версия, которую вы написали.

person Jim Mischel    schedule 05.11.2013
comment
Извините, но это опечатка, фактический код while (bytesProcessed<=total && bytesRead>=0 ); - person Arpit; 06.11.2013
comment
Работа с одним httpWebRequest будет моим последним средством, но мне любопытно, почему потоки выходят одновременно. Выполняя дополнительную отладку, t1 и t2 отлично работают вместе, но проблема связана с t3. это право делать HttpWebRequest hr3 = hr1; hr3.AddRange(e2,e3); - person Arpit; 06.11.2013
comment
Нет, нельзя делать hr3 = hr1; В итоге вы делаете два запроса от одного HttpWebRequest объекта. Результат этого действия будет непредсказуем. Почему вы настаиваете на использовании нескольких запросов, когда ясно, что один запрос является лучшим решением, мне не понятно. Но это ваш код и ваше время. Повеселись. - person Jim Mischel; 06.11.2013

Я согласен с @Kyle Pittman. Скорее всего, причиной вашей проблемы являются переменные BytesRead и BytesProcessed.

person Michel Vaz Ramos    schedule 05.11.2013
comment
Нет, они копируются по значению при каждом вызове метода. - person Servy; 06.11.2013
comment
Где это объявление этих переменных? Откуда он доступен? Я думаю, стоит проверить это перед чем-либо. И, пожалуйста, отмените этот отрицательный голос на мой ответ, я пытаюсь ему помочь. - person Michel Vaz Ramos; 06.11.2013
comment
Почему бы вам не заглянуть в код и не убедиться самому? Это прямо в коде. Если вы даже не знаете, где объявлена ​​переменная, то почему вы утверждаете, что знаете ответ на вопрос? Вы несете ответственность как лицо, отвечающее, чтобы доказать, что ваш ответ правильный. Объясните нам, точно как эти переменные вызывают проблему. Я понизил ваш ответ, потому что он неправильный, а также потому, что он бесполезен и не объясняет фактическую ошибку или то, как ее исправить. Если вы исправите его, чтобы он действительно был правильным, я бы рассмотрел вопрос об отмене своего голоса. - person Servy; 06.11.2013
comment
На самом деле в его коде нет объявления переменных bytesRead и BytesProcessed, поэтому я говорю ему сначала посмотреть на эти переменные. Вы ошибаетесь: я не утверждаю, что знаю ответ на вопрос, но я обязан помочь ему найти ответ или найти способы, чтобы он мог найти ответ сам. Именно в этот момент вы потерпели неудачу, проголосовав за мою помощь. Я думаю, вы нажимаете быстрее, чем вы думаете. - person Michel Vaz Ramos; 20.11.2013
comment
Эти переменные являются параметрами метода. Вам нужно только взглянуть на сигнатуру метода, чтобы увидеть их. Если вы действительно боретесь, просто используйте Ctrl + F, чтобы помочь вам найти их. К сожалению, из-за плохого форматирования кода вам нужно прокрутить вправо, чтобы увидеть их, но они действительно есть. Хотя понятно, что вы его пропустили, и очевидно, что вы не единственный, кто это сделал сначала, как только несколько других нашли его, вам действительно следовало присмотреться повнимательнее. - person Servy; 20.11.2013
comment
Далее, если вы не знаете ответа, но просто хотите добавить некоторую информацию, вы должны опубликовать комментарий, а не ответ. Ответы предназначены исключительно для ответа на вопрос. Если вы хотите добавить что-то, что не является ответом, вам следует публиковать комментарий, а не ответ. - person Servy; 20.11.2013