- PR -

マルチスレッドによるDrawImageについて

投稿者投稿内容
たけうち
会議室デビュー日: 2007/12/13
投稿数: 7
投稿日時: 2007-12-13 11:44
Graphics.DrawImageメソッドについて分からない事があり、皆様の知恵を拝借できればと思います。

分からない事というのは、各スレッドで異なるGraphicsインスタンスのDrawImageメソッドを呼び出しているのですが、この呼び出しが並列実行されていないようなのです。

なぜそう思うかというと、当方の環境はクワッドCPUなのですが、タスクマネージャでCPU使用率を見ると25%以上にならないのです。

現状、4スレッドで並列実行しているので、理論的には100%になるハズなんですが…

試しに、DrawImageの呼び出し部分を単純なFor文(中身は適用な演算)に変更すると、CPU使用率は100%になります。
ですので、スレッドとしては、正しく動いているようなのです。

この事から、DrawImageメソッドが内部的に全インスタンスで同期をとっているのだと思うのですが、
なぜ、同期をとる必要があるかのが分かりません。

また、DrawImageメソッドを呼び出している間、CPU使用率は25%なのにGUIの更新が極端に重たくなるという現象も発生します。
(DrawImageはワーカースレッドで呼び出しているのでメインスレッドとは別です。)

環境:VS2005、C#

この変の情報をお持ちの方はいらっしゃらないでしょうか?

よろしくお願いします。


すみません、先ほど違う場所に投稿してしまいましたので、こちらに再投稿させて頂きます。
甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2007-12-13 13:03

Graphicsインスタンスって何に対するインスタンスですか?もし画面に対するインスタンスなら、画面(をコントロールしているビデオドライバやビデオチップ)は一系統しかないのだから、そこがボトルネックになっているのでしょうね。

ちなみに画面への描画命令はキューがあふれるまでは非同期で実行されると思います。なので意味も無くループで書くと無駄に負荷が高くなりますよ。実際に画面に反映されるタイミングは、メソッドから戻ってくるタイミングより遅いです。
たけうち
会議室デビュー日: 2007/12/13
投稿数: 7
投稿日時: 2007-12-13 13:18
甕星さん返信ありがとうございます。

すみません、説明不足でした。
Graphicsインスタンスは、画像ファイルです。
プログラムですることは、特定のフォルダにある全画像ファイルのリサイズです。
それをマルチスレッドで処理したかったのです。
あぶぽん
大ベテラン
会議室デビュー日: 2005/10/20
投稿数: 205
投稿日時: 2007-12-19 13:42
ファイルIOが同期なのではないでしょうか?

ファイルシステムには詳しくありませんが、
物理的にHDDが一つなら非同期で動ける気がしませんが。

ちなみに、.Net Frameworkが提供している非同期ファイルIOというのは、
同時に複数のファイルにアクセスできるという訳ではなく、
ファイルアクセス中もGUIの更新などの処理ができるというものですね。

ただ、
「同時に多数の I/O 要求を保留できるためバフォーマンスを向上させます」
とは、書かれていますね↓

http://msdn2.microsoft.com/ja-jp/library/kztecsys(VS.80).aspx
たけうち
会議室デビュー日: 2007/12/13
投稿数: 7
投稿日時: 2007-12-19 14:09
あぶぽんさん返信ありがとうございます。

当初、私もファイルIOだと思い、色々とデバッグプログラムを作って検証したところ、
その結果、DrawImageが同期をとっているような動きをしているのです・・・

下記がデバックプログラムのソースです。
これを動かしてタスクマネージャを見るとクワッドCPUだと25%までしか使用されないのです・・・

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;

namespace TestDrawImage
{
 class Program
 {
  static void Main(string[] args)
  {
   for (int i = 0; i < 4; i++)
   {
    System.Threading.Thread trd = new System.Threading.Thread(ThreadTest);
    trd.Start();
   }
  }

  private static void ThreadTest()
  {
   using (FileStream fs = File.OpenRead("イメージファイル"))
   {
    using (Image orgImage = Image.FromStream(fs, false, false))
    {
     using (Bitmap newImage = new Bitmap(200, 200))
     {
      using (Graphics g = Graphics.FromImage(newImage))
      {
       for (int i = 0; i < int.MaxValue; i++)
       {
        g.DrawImage(orgImage, 0, 0, 200, 200);
       }
      }
     }
    }
   }
  }
 }
}
あぶぽん
大ベテラン
会議室デビュー日: 2005/10/20
投稿数: 205
投稿日時: 2007-12-19 14:23
たけうちさん、まいどです。

引用:

   using (FileStream fs = File.OpenRead("イメージファイル"))
   {
    using (Image orgImage = Image.FromStream(fs, false, false))
    {
    }
   }



ここでファイル開いてますよね?

そして、using句を抜けるまで、開きっぱなしってことですよね。
この行をなくしたら、非同期になりません?

コード:
Bitmap bitmap = new Bitmap("mypicture.jpg")


http://www.atmarkit.co.jp/fdotnet/dotnettips/018loadbmp/loadbmp.html

とかならOKかと思いますが。。

たしかではありませんが、DrawImage自体は同期してないと思います
というか、そのつもりでずっとプログラム書いてますが。。
たけうち
会議室デビュー日: 2007/12/13
投稿数: 7
投稿日時: 2007-12-19 14:38
あぶぽんさん、再度ありがとうございます。

Bitmap bitmap = new Bitmap("mypicture.jpg")

に変えたところ、CPU使用率が40%になりました。
なんか微妙です…

Image.FromStreamメソッドで開いた場合、DrawImageで毎回ファイルIOが発生していると言うことになるのでしょうか…

イメージの開き方を色々と変えて実験してみます!
あぶぽん
大ベテラン
会議室デビュー日: 2005/10/20
投稿数: 205
投稿日時: 2007-12-19 15:06
たけうちさん、ごくろうさまです。

引用:

Image.FromStreamメソッドで開いた場合、DrawImageで毎回ファイルIOが発生していると言うことになるのでしょうか…



というか、

コード:
using (FileStream fs = File.OpenRead("イメージファイル"))



で開いたファイルが開きっぱなしでしょ?

その中でループしてるから。。


冷静に考えたら

ここのサンプルなんかは、using句内で処理してますが、
http://www.atmarkit.co.jp/fdotnet/dotnettips/597fastloadimg/fastloadimg.html

ストリームはCloseしちゃっていいんじゃにですか?
http://jeanne.wankuma.com/tips/image/picturebox.html


あと、100%にはならない気がします。

newImageに描画してますから、きっとGDIとか使ってますよね。。

スキルアップ/キャリアアップ(JOB@IT)