【问题标题】:Threads are not responding线程没有响应
【发布时间】:2013-11-05 20:31:35
【问题描述】:

我尝试在 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

应用程序 ui 保持响应,并且通过调试我意识到每个线程同时退出。所以问题是为什么其他线程没有完成它们的部分。

【问题讨论】:

  • BytesRead 和 BytesProcessed 在 3 个线程之间通过引用共享?导致您的 while() 在 1 个线程完成后下载失败?
  • @KylePittman 不;它的写法很混乱,但是当调用方法时,这些变量的值会被复制到参数的存储中。每个线程都有自己的变量副本。不过,该代码非常具有误导性。
  • @KylePittman 但我猜这是一个简单的按值调用..
  • 你真的不应该锁定所有重要的工作;它几乎违背了创建多个线程的目的。除了在任何给定的时间点坐在那里什么都不做之外,只有一个人在做任何事情。实际上,如果另一个停止并阻塞读取,您甚至无法让其中一个工作。
  • 另外,您正在尝试下载 single 文件。这根本无法真正并行化。从同一个流中读取的三个线程并不比一个线程快;网络是瓶颈。并且发出三个不同的网络请求,以便三个线程可以分别读取三分之一的数据只会减慢您的速度,因为瓶颈(网络连接)必须完成三倍的工作。

标签: c# multithreading webrequest webresponse


【解决方案1】:

您在 download 方法的循环结束时有此条件:

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

那是你的问题。第一次通过循环,bytesProcessed 增加读取的字节数,使其大于0,导致条件为假,循环退出。

您永远不会在循环中进行一次以上的迭代。

顺便说一句,并不是每个响应都会有一个有效的ContentLength。网页通常不提供 Content-Length 标头,在这种情况下,ContentLength 属性将为 -1。即使页面确实提供了 Content-Length 标头,也不能保证它是正确的。因此,在一般情况下,您不能保证您已收到全部内容。

正如 Servy 在他的评论中指出的那样,在多个线程之间拆分下载不太可能提高性能,事实上很可能会导致下载速度变慢。如果站点看到您发出三个并发请求并决定限制或阻止您,则尤其如此。一般来说,您最好使用单个HttpWebRequest 进行正常下载。它更可靠,它简化了您的代码,并且可能会比您编写的过于复杂的多线程版本执行得更好。

【讨论】:

  • 抱歉打错了,实际代码是while (bytesProcessed&lt;=total &amp;&amp; bytesRead&gt;=0 );
  • 使用单个httpWebRequest 将是我的最后手段,但我很好奇为什么线程同时退出。进行一些 mor 调试,t1 和 t2 完美地一起运行,但问题出在 t3 上。有权利做HttpWebRequest hr3 = hr1; hr3.AddRange(e2,e3);
  • 不,不可以这样做 hr3 = hr1; 你最终会从一个 HttpWebRequest 对象发出两个请求。这样做的结果将是不可预测的。当很明显单个请求是更好的解决方案时,为什么还要坚持使用多个请求,这超出了我的理解。但这是你的代码和你的时间浪费。玩得开心。
【解决方案2】:

我同意@Kyle Pittman 的观点。很可能 BytesRead 和 BytesProcessed 变量是您的问题的原因。

【讨论】:

  • 不,每次调用方法时都是按值复制
  • 这个变量的声明在哪里?从哪里可以访问?我认为值得先检查一下。并且,请撤消对我的回答的反对票,我正在努力帮助他。
  • 为什么不自己看一下代码呢?它就在代码中。如果您甚至不知道变量的声明位置,那么您为什么声称知道问题的答案?作为回答者,您有责任证明您的回答是正确的。向我们准确地解释这些变量是如何导致问题的。我对您的回答投了反对票因为它是错误的,还因为它没有帮助并且没有解释实际错误或如何解决它。如果您将其修复为实际上是正确,那么我会考虑撤销我的投票。
  • 实际上,在他的代码中并没有声明变量 bytesRead 和 BytesProcessed,这就是为什么我告诉他首先查看这些变量的原因。你错了:我并没有声称知道问题的答案,但我有责任帮助他找到答案或想办法让他自己找到答案。正是在这一点上,您因否决了我的帮助而失败了。我猜你点击的速度比你想象的要快。
  • 这些变量是方法的参数。您只需要查看方法签名即可看到它们。如果您真的很苦恼,只需使用 ctrl+f 来帮助您找到它们。遗憾的是,由于代码格式不佳,您需要向右滚动才能看到它们,但它们确实存在。虽然错过它是可以理解的,而且很明显你不是唯一一个开始这样做的人,但一旦其他几个人发现它,你真的应该仔细看看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多