4

ウィキペディアのローカルデータベースコピーの記事を循環するアプリケーションをC#で作成しています。たくさんの正規表現を使用して、これらの記事から適切な情報を見つけ、スレッドを起動して各記事の画像を取得し、情報を保存して次の記事に進みます。

グーグルから禁止されないようにこれらの画像をダウンロードするにはプロキシのリストを使用する必要があります。プロキシが遅くなる可能性があるため、スレッドを使用して並列ダウンロードを行います。

スレッドを使用しない場合、アプリケーションは正常に機能していますが、すべての情報を取得するには時間がかかります。

スレッドを使用する場合、アプリケーションは約500スレッドを使用するまで動作し、その後OutOfMemory例外が発生します。

重要なのは、RAMを約300Moしか使用しないため、使用可能な合計メモリ(8Go)と単一の32ビットアプリケーションに割り当てられたメモリのいずれかのすべてのメモリを使用することはないということです。

アプリケーションごとのスレッドに制限はありますか?

編集:

ポスターをダウンロードするためのコードは次のとおりです(getPosterAsc()で開始)。

    string ddlValue = "";
    private void tryDownload(object obj)
    {
        WebClient webClientProxy = new WebClient();
        Tuple<WebProxy, int> proxy = (Tuple<WebProxy, int>)((object[])obj)[0];
        if (proxy != null)
            webClientProxy.Proxy = proxy.Item1;
        try
        {
            ddlValue = webClientProxy.DownloadString((string)((object[])obj)[1]);
        }
        catch (Exception ex) { 
            ddlValue = "";

            Console.WriteLine("trydownload:" + ex.Message);
        }

        webClientProxy.Dispose();
    }

    public void getPoster(object options = null)
    {
        if (options == null)
            options = new object[2] { toSave, false };
        if (!AppVar.debugMode && AppVar.getImages && this.getImage)
        {
            if (this.original_name != "" && !this.ambName && this.suitable)
            {
                Log.CountImgInc();

                MatchCollection MatchList;
                string basic_options = "";
                string value = "";
                WebClient webClient = new WebClient();
                Regex reg;
                bool found = false;

                if (original_name.Split(' ').Length > 1) image_options = "";

                if (!found)
                {
                    bool succes = false;
                    int countTry = 0;
                    while (!succes)
                    {
                        Tuple<WebProxy, int> proxy = null;
                        if (countTry != 5)
                            proxy = Proxy.getProxy();

                        try
                        {
                            Thread t = new Thread(tryDownload);
                            if (!(bool)((object[])options)[1])
                                t.Start(new object[] { proxy, @"http://www.google.com/search?as_st=y&tbm=isch&as_q=" + image_options + "+" + basic_options + "+" + image_options_before + "%22" + simplify(original_name) + "%22+" + " OR %22" + original_name + "%22+" + image_options_after + this.image_format });
                            else
                                t.Start(new object[] { proxy, @"http://www.google.com/search?as_st=y&tbm=isch&as_q=" + image_options + "+" + basic_options + "+" + image_options_before + "%22" + simplify(original_name) + "%22+" + " OR %22" + original_name + "%22+" + image_options_after + "&biw=1218&bih=927&tbs=isz:ex,iszw:758,iszh:140,ift:jpg&tbm=isch&source=lnt&sa=X&ei=kuG7T6qaOYKr-gafsOHNCg&ved=0CIwBEKcFKAE" });
                            if (!t.Join(40000))
                            {
                                Proxy.badProxy(proxy.Item1.Address.Host, proxy.Item1.Address.Port);
                                continue;
                            }
                            else
                            {
                                value = ddlValue;
                                if (value != "")
                                    succes = true;
                                else
                                    Proxy.badProxy(proxy.Item1.Address.Host, proxy.Item1.Address.Port);
                            }
                        }
                        catch (Exception ex)
                        {
                            if (proxy != null)
                                Proxy.badProxy(proxy.Item1.Address.Host, proxy.Item1.Address.Port);
                        }
                        countTry++;
                    }

                    reg = new Regex(@"imgurl\=(.*?)&amp;imgrefurl", RegexOptions.IgnoreCase);
                    MatchList = reg.Matches(value);
                    if (MatchList.Count > 0)
                    {
                        bool foundgg = false;
                        int j = 0;
                        while (!foundgg && MatchList.Count > j)
                        {
                            if (MatchList[j].Groups[1].Value.Substring(MatchList[j].Groups[1].Value.Length - 3, 3) == "jpg")
                            {
                                try
                                {
                                    string guid = Guid.NewGuid().ToString();
                                    webClient.DownloadFile(MatchList[j].Groups[1].Value, @"c:\temp\" + guid + ".jpg");

                                    FileInfo fi = new FileInfo(@"c:\temp\" + guid + ".jpg");
                                    this.image_size = fi.Length;

                                    using (Image img = Image.FromFile(@"c:\temp\" + guid + ".jpg"))
                                    {
                                        int minHeight = this.cov_min_height;
                                        if ((bool)((object[])options)[1])
                                            minHeight = 100;

                                        if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Jpeg) && img.HorizontalResolution > 70 && img.Size.Height > minHeight && img.Size.Width > this.cov_min_width && this.image_size < 250000)
                                        {
                                            foundgg = true;
                                            image_name = guid;
                                            image_height = img.Height;
                                            image_width = img.Width;
                                            img.Dispose();
                                            if ((bool)((object[])options)[0])
                                            {
                                                Mediatly.savePoster(this, (bool)((object[])options)[1]);
                                            }
                                        }
                                        else
                                        {
                                            img.Dispose();
                                            File.Delete(@"c:\temp\" + guid.ToString() + ".jpg"); 
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                }
                            }

                            j++;
                        }
                    }
                }

                webClient.Dispose();
                Log.CountImgDec();
            }
        }
    }

    public void getPosterAsc(bool save = false, bool banner = false)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(getPoster), new object[2] { save, banner });
    }
4

4 に答える 4

3

スレッドプールを使用してスレッドを「管理」していることを確認します。誰かが言ったように、各スレッドは約1MBのメモリを消費し、システムハードウェアによっては、これが問題を引き起こす可能性があります。

この問題を解決するための1つの潜在的な方法は、スレッドプールを使用することです。これにより、可能な場合はスレッドを共有してリサイクルすることにより、すべてのスレッドを生成することによって発生するオーバーヘッドが削減されます。これにより、低レベルのスレッド機能(多くのスレッドがアクティブになっている)が可能になりますが、そうすることによるパフォーマンスの低下が制限されます。

スレッドプールは、同時に実行されるワーカースレッドの数にも制限を設けています(これらはすべてバックグラウンドスレッドになることに注意してください)。運用スレッドが多すぎると、管理上のオーバーヘッドが大きくなり、「CPUキャッシュが無効になる」可能性があります。課すスレッドプールの制限に達すると、追加のジョブがキューに入れられ、別のワーカースレッドが解放されたときに実行されます。これは、あなたが必要とすることを行うためのはるかに効果的で、より安全で、リソース効率の良い方法だと思います。

現在のコードに応じて、スレッドプールに入る方法はいくつかあります。

  1. BackgroundWorker
  2. ThreadPool.QueueUserWorkItem
  3. 非同期のデリゲート。
  4. PLINQ。

個人的には素晴らしいのでTPLを使用します!これがお役に立てば幸いです。

于 2012-05-23T10:05:33.103 に答える
1

perfmonを使用して、実際にメモリを使用しているものを確認します。特に、「ModifiedPageListBytes」の値に細心の注意を払ってください。これは、参照が特定の期間ファイルに保持されているマルチスレッドアプリケーションでは特に厄介な場合があります。この値を頻繁に使用するための通常の(一時的な)解決策は、使用可能な仮想メモリを増やすことです。

また、Windows Server 2008で高度にスレッド化されたアプリケーションを実行している場合は、システムファイルキャッシュが使用可能なメモリを効果的に消費しないように、Microsoftのdynacacheを適用する必要があります。

上記の問題は両方とも、大量のデータを処理する.netマルチスレッドアプリケーションに直接関連している可能性がありますが、残念ながら、アプリケーションで使用されているとは表示されず、その結果、追跡が困難になる可能性があります(痛みを伴う数日間のコース)

于 2012-05-23T12:54:10.807 に答える
0

32ビットの実行可能ファイルを使用する場合、実際にはデフォルトで2Gbのみを割り当てることができ、8Gbは割り当てることができません(詳細については、http://blogs.msdn.com/b/tom/archive/2008/04/10/chat-question-を参照してください)。 memory-limits-for-32-bit-and-64-bit-processes.aspx

作業スレッドを制限して、その数を使用しないようにし、スレッドで実行されるコードでメモリリークが発生しないようにしてください。

ダウンロードした画像に関係している可能性があるため、スレッド実行をtry ... catch(スレッド実行コードでOutOfMemoryExceptionが発生した場合)でラップします。

于 2012-05-23T08:58:18.807 に答える
0

私は最近、これに非常によく似たアプリケーションの1つで問題に遭遇しました。これは、単一の「文字列」オブジェクトに格納および使用されるデータの量と関係がありました。推測しなければならない場合、メモリ不足の例外は、

ddlValue = webClientProxy.DownloadString((string)((object[])obj)[1]);

そうするように書き直すことができれば、応答全体を文字列に読み込むのではなく、ストリームとしてWebリターンにアクセスする方法を見つけることができますか。次に、ストリームリーダーを使用して行ごとにWeb応答を解析できます。

はい、これは非常に複雑に聞こえますが、自分のコードで使用しなければならなくなったソリューションと一致します。大きすぎて単一の文字列として保存できないものを扱っていたため、代わりにストリームから直接アクセスする必要がありました。

于 2012-05-23T13:02:10.127 に答える