none
複数スレッドでの Shared imgconv As System.Drawing.ImageConverter() 使用方法 RRS feed

  • 質問

  • 表題の件ですが、現在複数スレッドで非同期にByte→Imageへの変換、その逆を行いたいと思っております。

    Shared imgconv As System.Drawing.ImageConverter()を使用するとimgconvがスレッド間で競合を起こし

    エラーが発生しております。StaticなLockオブジェクトを指定することで回避はできましたが

    Lockをしている時間は他のスレッドが待ち状態になってしまい変換時間に無駄が生じてしまいます。

    コードは下記となります。

      Shared imgconv As New System.Drawing.ImageConverter()

            Shared lockObject As New Object() '
            ' バイト配列をImageオブジェクトに変換
            Public Function ByteArrayToImage(ByVal b As Byte()) As Image
                SyncLock lockObject
                    Dim sw As New Stopwatch
                    sw.Restart()
                    'Dim imgconv As New System.Drawing.ImageConverter
                    Dim img As Image = CType(imgconv.ConvertFrom(b), Image)
                    sw.Stop()
                    Console.WriteLine("ByteArrayToImage:" & sw.ElapsedMilliseconds & vbCrLf)
                    Return img
                End SyncLock
            End Function

     

    お詳しい方が見えましたらマルチスレッドからのアクセス方法、もしくは他の変換方法(ConvertFromとConvertToの代わりになるもの)

    をご教授願いたく、どうぞよろしくお願いいたします。

    2017年9月28日 8:22

すべての返信

  • なぜImageConverterをsharedにして複数スレッド間で使いまわす必要があるのでしょう?

    ImageConverterの説明には「この型のパブリック static (Visual Basic では Shared ) メンバーはスレッド セーフです。インスタンス メンバーの場合は、スレッド セーフであるとは限りません。 」と書かれているので、複数スレッドからの同時アクセスは保証されてません。

    回避するなら、スレッド毎にImageConverterを作ればいいだけです。

    Public Class Form1
        Sub New()
    
            ' この呼び出しはデザイナーで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後で初期化を追加します。
    
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim buf As Byte()
            Using fs As System.IO.Stream = System.IO.File.OpenRead("test.jpg")
                ReDim buf(fs.Length)
                fs.Read(buf, 0, buf.Length)
            End Using
    
            For i = 0 To 10
                Dim thread As New System.Threading.Thread(Sub()
                                                              Dim imgconv As New ImageConverter
                                                              ByteArrayToImage(buf, imgconv)
                                                          End Sub)
                thread.Start()
            Next
        End Sub
    
        Public Function ByteArrayToImage(ByVal b As Byte(), Optional ByVal imgconv As System.Drawing.ImageConverter = Nothing) As Image
    
            If (imgconv Is Nothing) Then
                imgconv = New ImageConverter()
            End If
    
            Dim sw As New Stopwatch
            sw.Restart()
            Dim img As Image = CType(imgconv.ConvertFrom(b), Image)
            sw.Stop()
            Console.WriteLine("ByteArrayToImage:" & sw.ElapsedMilliseconds & vbCrLf)
            Return img
    
        End Function
    End Class

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年9月28日 9:17
  • ImageConverterクラスはTypeConverterの派生クラスとして、統一された変換方法を提供するためのものであり、内容的には Image.FromStream(New MemoryStream(b)) でしかありません。ImageConverterクラスを使うまでもないのでは?
    2017年9月28日 13:57
  • gekka様

    ご指示ありがとうございます。

    おっしゃることはもっともで、すでにその方法も試してみましたが

    やはりオブジェクトが重複する旨のエラーが発生しております。(Gekka様記載方法も試しました)

    現在コーディングしているのはWWFの非同期なActivityを自作しております。

    このActibity内にimgconvを使用する記述があるのですがActivity事態をワークフローに

    並列に配置して2~3個のActibityを実行する際に発生してます。

    Activityならではの、何か問題(インスタンスがNewできていない)のかなとは考えておりますが

    原因がいまだにわかりません。

    いかがでしょうか?

    2017年9月29日 1:19