C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

画像のピクセル数をマルチスレッドで使用するとエラーが出てしま

[トピック内 4 記事 (1 - 4 表示)]  << 0 >>

■89503 / inTopicNo.1)  画像のピクセル数をマルチスレッドで使用するとエラーが出てしま
  
□投稿者/ ゆさこ (1回)-(2018/11/30(Fri) 20:01:08)

分類:[.NET 全般] 

VB.NETに関する質問です。

画像ファイルを読み込み、
その画像ファイルのピクセル数を変数として用いた
プログラムでマルチスレッドで書きたいのですが、

        Dim bmp2 As New Bitmap(FilePath)


        Parallel.For(0, bmp2.Height,
           Sub(y As Integer)

               Dim bmpWid2 As Integer = bmp2.Width - 1

           End Sub)

のようにすると


型 'System.AggregateException' のハンドルされていない例外が mscorlib.dll で発生しました

追加情報:1 つ以上のエラーが発生しました。


というエラーが発生します。


bmp2.Widthを予め別の変数に代入してから
マルチスレッドで使用するとエラーが出なくなります。

これは一体なぜなのでしょうか?






引用返信 編集キー/
■89504 / inTopicNo.2)  Re[1]: 画像のピクセル数をマルチスレッドで使用するとエラーが出てしま
□投稿者/ Hongliang (724回)-(2018/11/30(Fri) 20:12:30)
そういうものだからとしか言いようがないですね。
Bitmapはマルチスレッドで安全に扱えるような設計ではなさそうです。
引用返信 編集キー/
■89511 / inTopicNo.3)  Re[1]: 画像のピクセル数をマルチスレッドで使用するとエラーが出てしま
□投稿者/ 魔界の仮面弁士 (1967回)-(2018/12/01(Sat) 00:50:27)
2018/12/01(Sat) 01:31:01 編集(投稿者)

No89503 (ゆさこ さん) に返信
> Dim bmp2 As New Bitmap(FilePath)
> Parallel.For(0, bmp2.Height,
>  Sub(y As Integer)
>   Dim bmpWid2 As Integer = bmp2.Width - 1
>  End Sub)

Bitmap インスタンスを生成したスレッドと
Width プロパティにアクセスしているスレッドが
異なるからでは無いでしょうか。
(ワーカースレッド内でインスタンス化した場合にどうなるかは未検証です)


そもそも GDI+ がマルチスレッドに対応していないのではないでしょうか。
STA を前提としているようですし、MSDN Library によれば、System.Drawing 名前空間は、
Windows や ASP.NET サービス内での使用がサポートされていないとも書かれていました。


そして先のコードを API レベルで言えば、GdipCreateBitmapFromFile で取得した GpImage を
ワーカースレッド内で GdipGetImageWidth に渡して処理することになりますね。
このように GpImage がスレッド間で共有される事自体、望ましくない処理だと思います。


ちなみに GDI+ に関しては、こんな話もありました。
System.Drawing.Bitmap の話では無いですが、一応念のため。
https://blogs.msdn.microsoft.com/japan_platform_sdkwindows_sdk_support_team_blog/2017/10/10/gdiplus-tsf-memleak/
https://docs.microsoft.com/en-us/windows/desktop/api/gdiplusinit/nf-gdiplusinit-gdiplusstartup
引用返信 編集キー/
■89514 / inTopicNo.4)  Re[1]: 画像のピクセル数をマルチスレッドで使用するとエラーが出てしま
□投稿者/ Azulean (1018回)-(2018/12/01(Sat) 09:35:08)
No89503 (ゆさこ さん) に返信
> Parallel.For(0, bmp2.Height,

雰囲気的には Y 方向で並列実行し、1処理単位で X 方向の1ラインを処理するという感じですかね?
これが正しいとすると、GDI+ のオブジェクトはスレッドセーフではないという前提にたって、複数スレッドで同時にアクセスできる形に落とし込む必要がありそうです。

たとえば、ピクセルのデータにアクセスする処理を並列化するなら、サイズの情報をローカル変数に代入し LockBits で固定したメモリに対する並列アクセスにしたら良いかと思います。

引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ