2

この質問は一般的すぎるかもしれません。しかし、私は多くのことを試しましたが、これを解決する方法を理解できません。

マルチスレッド操作に ConcurrentQueue を使用しています。1 つのスレッドがサーバーから画像をダウンロードし、キューに保存しています。そのためのコードは次のとおりです。

 public static void DownloadImage()
    {
        string baseUrl = "http://someurl";
        //int numIterations = 5;

        HttpWebRequest request = null;
        foreach (var fileName in fileNames)
        {
                string url = string.Format(baseUrl, fileName);
                request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = "GET";
                request.ContentType = "application/x-www-form-urlencoded";
                var response = (HttpWebResponse)request.GetResponse();
                Stream stream = response.GetResponseStream();
                img = Image.FromStream(stream);
                ImageFileName FileNameImage = new ImageFileName(fileName, img);
                ImageQueue.Enqueue(FileNameImage);
                Console.WriteLine("Count after Enqueue: {0}", ImageQueue.Count);

         }

そして、別のスレッドがキューから画像を取得し、宛先フォルダーに保存します。そのためのコードは次のとおりです。

public static void SaveImage()
    {
        while (true)
        {
            if (!ImageQueue.IsEmpty)
            {
                foreach (var newobject2 in ImageQueue)
                {

                    Image img2 = newobject2.Image;
                    img2.Save("C:\\path" + newobject2.ImageName);
                    ZoomThumbnail = img2;
                    ZoomSmall = img2;
                    ZoomLarge = img2;

                    ZoomThumbnail = GenerateThumbnail(ZoomThumbnail, 86, false);
                    ZoomSmall = GenerateThumbnail(ZoomSmall, 400, false);
                    ZoomLarge = GenerateThumbnail(ZoomLarge, 1200, false);

                    ZoomThumbnail.Save("C:\\path" + newobject2.ImageName + "_Thumb.jpg");
                    ZoomSmall.Save("C:\\path" + newobject2.ImageName + "_ZoomSmall.jpg");
                    ZoomLarge.Save("C:\\path" + newobject2.ImageName + "_ZoomLarge.jpg");
                    ImageFileName imgobject3 = new ImageFileName();
                    ImageQueue.TryDequeue(out imgobject3);
                    Console.WriteLine("Count after Deque: {0}", ImageQueue.Count);

                }


            }


        }

    }

この 2 つのスレッドを Button_Click() から次のように呼び出しています。

Thread DownloadThread = new Thread(DownloadImage);
  DownloadThread.Start();
  Thread SaveThread = new Thread(SaveImage);
  SaveThread.Start();

キューのカウントが 68 に達するたびに MemoryFull エラーが発生します。これを回避する方法がわかりません。これを回避するために Thread.Sleep を使用してみました。たとえば、私が試したのは: Thread.Sleep(500)after foreachloop. 内部foreachで試してみると、どの時点でもまったく問題なく動作しますImageQueue.Count = 1。どこが間違っていますか?

4

5 に答える 5

1

実際にキューから画像を削除することはありません。それらを反復処理しますが、それらをデキューすることはありません。また、キューを列挙するときに、現在キューにアイテムがなくなるたびに列挙可能が停止するという問題もあります。あなたはこれをしたくありません、あなたはまだ終わっていないかもしれません.

あなたがしたいことは、 のBlockingCollection代わりに を使用することです。これにより、現在ConcurrentQueueアイテムがなくなった場合、停止するのではなく、さらに待つようになります。これを行うと、使用できます。これにより、必要な変更がすべて行われます。反復するときにキューからアイテムを削除し、現在アイテムがない場合はさらにアイテムを待ちます。foreach(var item in queue.GetConsumingEnumerable())

Guffa の回答に記載されているように、この変更を行うことに加えて、画像オブジェクトを破棄する必要があります。画像の処理が完了したら、ループ本体の最後でそれらを破棄できます。

于 2014-04-07T15:44:05.943 に答える
0

この種の「生産者と消費者」のパターンに使用して成功したBlockingCollectionクラス (こちら) を参照してください。

何か (プロデューサー) がアイテムをコレクションに入れ、1 つ以上の「コンシューマー」b/g スレッドがコレクションから次に利用可能なアイテムを取得し、それらに対して何かを行います。使用するコンシューマー スレッドの数を構成することもできます (マルチコア PC など)。

于 2014-04-07T15:53:26.257 に答える
0

1.ガベージコレクターがそれらを収集することを許可せずに、それらをメモリに保持するすべての画像への参照を保持しているようです。

また、オブジェクトの使用が終了したら、次のように明示的に破棄できます。

Image img2 = newobject2.Image;
img2.Dispose();

2. あなたがしているのは、while ループに入るたびにすべての画像を列挙することです。これがあなたの望むものかどうかはわかりません。あなたがすべきことは、アイテムをデキューしようとすることです:

FileImageName myImage;
ImageQueue.TryDequeue(out myImage);

if (myImage != null)
{
// Do work on the image
}
于 2014-04-07T15:45:20.480 に答える
0

画像の処理が完了したら、ZoomLarge ステートメントの後に破棄する必要があります。その後は使用しないためです。Generate ステートメントを使用して、新しいイメージを作成します。

于 2014-04-07T15:43:14.200 に答える
0

破棄しない多くのImageオブジェクトを作成しています。Dispose作成した各サムネイルを呼び出す必要があります。

また、デキューするImageオブジェクトには、不要になったときに破棄する必要があるオブジェクトが含まれています。


補足: キュー内の項目を列挙して、ループ内で項目をデキューしないでください。代わりTryDequeueに、ループ自体でメソッドを使用します。

ImageFileName imgobject2;
while (ImageQueue.TryDequeue(out imgobject2)) {
  ...
}

別の補足事項: キューが空の場合、コードはタイトなループに入り、大量の CPU パワーを使用して同じチェックを非常に迅速に繰り返します。キューで何かが発生するまでスレッドを一時停止するメカニズムを使用するか、キューを再度チェックする前に少なくともしばらくスリープ状態にする必要があります。

于 2014-04-07T15:35:34.717 に答える