DOBON.NET DOBON.NETプログラミング掲示板過去ログ

マルチスレッドでのダウンロードについて

環境/言語:[Windows XP C#.NET Framework1.0]
分類:[.NET]


はじめまして。初投稿させていただきます。

マルチスレッドでhtmlをダウンロードするとhtmlが途中までしか読み込まれないという現象が起こります。
同じコードでも、メインスレッド以外のスレッドが4つまでなら全文読み込みされているようですが5つ以上になると上記現象が起こります。
下記コードの
myStream=web.OpenRead(targetURL);
をロックしてしまえば全文読み込まれるのですが、ここをロックしてしまうとマルチスレッドの意味がなくなってしまうような気がするので他に手段があると思うのですが、その方法が分かりません。
何か良い方法があれば、お教えください。
よろしくお願いいたします。

public void myThreadstart()
{
int ThreadNumber=4;
Thread[] th=new Thread[ThreadNumber];
for(i=0;i<ThreadNumber;i++)
{
th[i] = new Thread(new ThreadStart(myThread));
th[i].Name=i.ToString();
th[i].Start();
}
}
private void myThread()
{
string targetURL;
string strHTML;
System.Net.WebClient web=new System.Net.WebClient();
System.IO.Stream myStream;
System.IO.StreamReader myStreamReader;

//各スレッド用の[targetURL]を取得するコード(lock)

myStream=web.OpenRead(targetURL);
myStreamReader=new _
System.IO.StreamReader(myStream,System.Text.Encoding.Default);
strHTML=myStreamReader.ReadToEnd();

//HTMLを解析・保存するコード
}
> 同じコードでも、メインスレッド以外のスレッドが4つまでなら全文読み込みされているようですが5つ以上になると上記現象が起こります。

関係あるかどうかはわかりませんが……。
http://support.microsoft.com/kb/183110/ja
http://www.losttechnology.jp/Tips/maxconnect.html
■No14200に返信(魔界の仮面弁士さんの記事)
>>同じコードでも、メインスレッド以外のスレッドが4つまでなら全文読み込みされているようですが5つ以上になると上記現象が起こります。
> > 関係あるかどうかはわかりませんが……。
> http://support.microsoft.com/kb/183110/ja
> http://www.losttechnology.jp/Tips/maxconnect.html

返信ありがとうございます。
教えて頂いたHPを参照させていただきました。
アプリケーション側からは、リクエストを待たされるだけで意識しなくても・・
といった内容のことが書かれていましたので、おそらく今回の件とは違うような気がします。

確認のために以下を試してみました。
全文読み込みできるスレッド数で作ったexeファイルを複製して同時起動する。
【結果】合計スレッド数が10でも20でも全文読み込み可能。

よって合計アクセス数の問題ではなく、マルチスレッドでのwebclientクラスの利用に何か問題があるように思うのですが、解決策が全く分かりません。
webclientクラスはスレッドセーフと書かれていたはずなのに・・・
> webclientクラスはスレッドセーフと書かれていたはずなのに・・・

スレッドセーフではありません。静的メンバのみ、スレッドセーフです。

しかしコードを拝見しますと、すべてローカル変数を使用しており、スレッド間でオブジェクトを共有しているということはないようですので、このコードを見る限りでは、スレッドセーフでないということとこの問題は関係ないようです。もしスレッド間でオブジェクトを共有しているようでしたら、ご注意ください。
■No14216に返信(管理人さんの記事)

管理人さん、返信ありがとうございます。
私の力不足でMSDNに書かれてある
「この型の public static (Visual Basicでは Shared) のすべてのメンバは、マルチスレッド操作で安全に使用できます。インスタンスのメンバの場合は、スレッドセーフであるとは限りません。」
http://www.microsoft.com/japan/msdn/library/?url=/japan/msdn/library/ja/cpref/html/frlrfsystemnetwebclientclasstopic.asp
という意味が正確に理解できていないかもしれません。
以下のコードでスレッドセーフで利用できていると思うのですが、保存される内容をチェックすると途中までしか読み込まれないファイルが存在します。
何か問題があるようでしたら、ご指摘お願いいたします。
(丸投げみたいになってしまい申し訳ありません。)

using System;

namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
mythreadStart();
}
public static void mythreadStart()
{
int ThreadNumber=10;
int i;
System.Threading.Thread[] th=new System.Threading.Thread[ThreadNumber];
for(i=0;i<ThreadNumber;i++)
{
th[i] = new System.Threading.Thread(new System.Threading.ThreadStart(myThread));
th[i].Name="ThreadNumber(" + i.ToString() + ")";
th[i].Start();
}
}
public static void myThread()
{
string strHTML;
System.Net.WebClient web=new System.Net.WebClient();
System.IO.Stream myStream;
System.IO.StreamReader myStreamReader;
System.IO.StreamWriter myWriter;
int counter=0;

myStream=web.OpenRead("http://www.yah●●.co.jp/");
myStreamReader=new System.IO.StreamReader(myStream,System.Text.Encoding.Default );
strHTML=myStreamReader.ReadToEnd();

myWriter=new System.IO.StreamWriter(new System.IO.FileStream(System.Threading.Thread.CurrentThread.Name + ".txt",System.IO.FileMode.Append));
System.Diagnostics.Debug.WriteLine("[" + System.Threading.Thread.CurrentThread.Name + "]" + counter.ToString());
myWriter.Close();
myStream.Close();
myStreamReader.Close();
}
}
}
前回も書かせていただいたように、このコードを見る限りでは、問題はありません。(ただ、ファイルに書き込むコードが抜けています。)

もしエラーも出さずに、途中までしか読み込まないということになると、理由は分かりません(バックグランドスレッドではないようですし...)。

既に十分されたのかもしれませんが、デバッグにより原因を探ることが必要ではないでしょうか。例えば、サーバーをいろいろ変えてみるとか(サーバー側の原因も考えられますので)、数バイトずつ読み込んでみるとか、別の方法でダウンロードしてみるとか、試してみてください。魔界の仮面弁士さんの方法を試すことなく否定するのもいかがなものかと思います。
■No14229に返信(管理人さんの記事)
> 前回も書かせていただいたように、このコードを見る限りでは、問題はありません。(ただ、ファイルに書き込むコードが抜けています。)
> > もしエラーも出さずに、途中までしか読み込まないということになると、理由は分かりません(バックグランドスレッドではないようですし...)。
> > 既に十分されたのかもしれませんが、デバッグにより原因を探ることが必要ではないでしょうか。例えば、サーバーをいろいろ変えてみるとか(サーバー側の原因も考えられますので)、数バイトずつ読み込んでみるとか、別の方法でダウンロードしてみるとか、試してみてください。

返信ありがとうございます。
申し訳ありません。書き込みコードの記入漏れがありました。
もう一度、最初から調べ直してみます。

>魔界の仮面弁士さんの方法を試すことなく否定するのもいかがなものかと思います。
弁解ですが、手放しで否定したつもりはありません。
誤解を招くような記述がありましたことをお詫びします。
Re[2]: にて評価した内容は
一つのスレッドで同じURLから繰り返しダウンロードし続けるEXEファイルを作成し、その複製EXEを10個同時起動しても、問題なくダウンロードされました。
という内容のものです。(勿論URLはすべて同じ)
別プロセスでは問題なく処理できている処理が別スレッドで処理できなくなるという結果より、短絡的にスレッドに問題があると判断してしまいました。

再度調査をし、何か分かりましたらアップさせていただきます。
横レスでごめんなさい。
本件のジャンルが僕の案件に似てたので、参考になればと思い、書き込みました。
[現象]
ひとつのEXEからスレッドを複数たててSystem.net.WebClientのインスタンスを各スレッドごとに立てて、ダウンロードすると、5個目がHTMLファイルを全てダウンロードできない。EXEを複数作成し、同時に走らせた場合は10以上同時でも可能。

↑これですが、サーバ側で同一セッションでの同時ダウンロード処理の制限ってありませんか?
FTPからダウンロードする場合は同一セッションの同時ダウンロードに制限がかかることあります。ちなみにEXEを複数起動するということは、セッションをEXEごと取りにいっていると思いますのでEXEを10以上起動して同時ダウンロードでも可能になると思います。

気になるのは、5スレッド目で途中までReadToEndが動作している点ですが、このような状況下でのReadToEndの動作がどうなるのか検証はしていません。絶対5スレッド目に起きているのかという現象確認も必要かもしれません。
System.net.WebClientでセッションの状況を確認する関数とかあればダウンロード可能状態になった段階でReadToEndを呼べばいいと思いますが、ちょっと忙しくてここまで調べる時間がありませんでした。


はずしてたら申し訳ありません。
■No14247に返信(かつどんさんの記事)


かつどんさん、返信ありがとうございます。

> > ↑これですが、サーバ側で同一セッションでの同時ダウンロード処理の制限ってありませんか?
> FTPからダウンロードする場合は同一セッションの同時ダウンロードに制限がかかることあります。ちなみにEXEを複数起動するということは、セッションをEXEごと取りにいっていると思いますのでEXEを10以上起動して同時ダウンロードでも可能になると思います。
セッション単位という考えが全くありませんでした。
それ以前にセッションというものに対して全くの無知でした。
基礎から勉強しなおして、複数セッションを取得する方法について検討してみようと思います。
ホント自分のレベルの低さが情けないです・・・

> > 気になるのは、5スレッド目で途中までReadToEndが動作している点ですが、このような状況下でのReadToEndの動作がどうなるのか検証はしていません。絶対5スレッド目に起きているのかという現象確認も必要かもしれません。
> System.net.WebClientでセッションの状況を確認する関数とかあればダウンロード可能状態になった段階でReadToEndを呼べばいいと思いますが、ちょっと忙しくてここまで調べる時間がありませんでした。
とりあえず現段階では、ReadEndで読み込んだ内容をチェックして最後まで読み込まれていなければ再読み込みを開始するというコードで乗り切りました。
セッションについての知識を深めた後に今回の現象についても詳細に調べ直してみます。
ありがとうございました。

DOBON.NET | プログラミング道 | プログラミング掲示板