none
求教,多线程问题 RRS feed

  • 问题

  • 这是一个由单线程修改来的多线程程序,求教如何修改才能正常运行
    Imports System.Math
    Imports System.Threading
    Imports VB = Microsoft.VisualBasic.DateAndTime
    
    Public Class frmMain
      Private m_Mandelbrot1, m_Mandelbrot2, m_Mandelbrot3, m_Mandelbrot4 As Mandelbrot
      Private m_Thread1, m_Thread2, m_Thread3, m_Thread4 As Thread
      Private m_dblTimerStart, m_dblTimerEnd As Double
    
      Private Sub btnDraw_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDraw.Click
        Dim dblCPMin, dblCPMax As Single
        Dim dblCQMin, dblCQMax As Single
        Dim dblEscapeTimeLimit As Integer
        Dim dblEscapeRadiusLimit As Integer
    
        'g_bmpBuffer = New Bitmap(1024, 768)
        'picFractal.Image = g_bmpBuffer
        'g_grDraw = Graphics.FromImage(g_bmpBuffer)
    
        dblCPMin = -2.2
        dblCPMax = 1.2
        dblCQMin = -1.4
        dblCQMax = 1.4
        dblEscapeTimeLimit = 50
        dblEscapeRadiusLimit = 500
        m_dblTimerStart = VB.Timer
    
        timThread.Enabled = True
    
        m_Mandelbrot1 = New Mandelbrot(dblCPMin, dblCPMax, dblCQMin, dblCQMax, dblEscapeTimeLimit, dblEscapeRadiusLimit, 0, 1024 / 4, picFractal)
        m_Thread1 = New Thread(AddressOf m_Mandelbrot1.Draw)
        m_Thread1.Start()
    
        m_Mandelbrot2 = New Mandelbrot(dblCPMin, dblCPMax, dblCQMin, dblCQMax, dblEscapeTimeLimit, dblEscapeRadiusLimit, 1024 / 4, 1024 / 4 * 2, picFractal)
        m_Thread2 = New Thread(AddressOf m_Mandelbrot2.Draw)
        m_Thread2.Start()
    
        m_Mandelbrot3 = New Mandelbrot(dblCPMin, dblCPMax, dblCQMin, dblCQMax, dblEscapeTimeLimit, dblEscapeRadiusLimit, 1024 / 4 * 2, 1024 / 4 * 3, picFractal)
        m_Thread3 = New Thread(AddressOf m_Mandelbrot3.Draw)
        m_Thread3.Start()
    
        m_Mandelbrot4 = New Mandelbrot(dblCPMin, dblCPMax, dblCQMin, dblCQMax, dblEscapeTimeLimit, dblEscapeRadiusLimit, 1024 / 4 * 3, 1024, picFractal)
        m_Thread4 = New Thread(AddressOf m_Mandelbrot4.Draw)
        m_Thread4.Start()
      End Sub
    
      Private Sub timThread_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timThread.Tick
        If m_Thread1.ThreadState = ThreadState.Stopped And m_Thread2.ThreadState = ThreadState.Stopped And m_Thread3.ThreadState = ThreadState.Stopped And m_Thread4.ThreadState = ThreadState.Stopped Then
          m_dblTimerEnd = VB.Timer
          timThread.Enabled = False
          MsgBox(m_dblTimerEnd - m_dblTimerStart)
          With picFractal
            .Image = g_bmpBuffer
            .Refresh()
          End With
        End If
      End Sub
    End Class
    
    Public Class Mandelbrot
      Private m_dblCP, m_dblCPInitial As Double
      Private m_dblCQ, m_dblCQInitial As Double
      Private m_dblZX, m_dblZY As Double
      Private m_dblEscapeTime As Double
      Private m_dblRadius As Double
    
      Private m_intColorR, m_intColorG, m_intColorB As Integer
      Private m_intR, m_intG, m_intB As Integer
      Private m_dblColor1, m_dblColor2 As Double
    
      Private m_sngCPMin, m_sngCPMax, m_sngCQMin, m_sngCQMax As Single
      Private m_intEscapeTimeLimit, m_intEscapeRadiusLimit As Integer
      Private m_srtWidthIndexStart, m_srtWidthIndexEnd As Short
      Private m_picDraw As PictureBox
    
      Public Sub New(ByVal CPMin As Single, ByVal CPMax As Single, ByVal CQMin As Single, ByVal CQMax As Single, ByVal EscapeTimeLimit As Integer, ByVal EscapeRadiusLimit As Integer,
              ByVal WidthIndexStart As Short, ByVal WidthIndexEnd As Short, ByVal MandelbrotPictureBox As PictureBox)
        m_sngCPMin = CPMin
        m_sngCPMax = CPMax
        m_sngCQMin = CQMin
        m_sngCQMax = CQMax
        m_intEscapeTimeLimit = EscapeTimeLimit
        m_intEscapeRadiusLimit = EscapeRadiusLimit
        m_srtWidthIndexStart = WidthIndexStart
        m_srtWidthIndexEnd = WidthIndexEnd
        m_picDraw = MandelbrotPictureBox
      End Sub
    
      Public Sub Draw()
    
        Dim dblCPRange, dblCQRange As Double
    
        Dim srtWidth, srtHeight As Short '绘图区高
        Dim srtWidthIndex, srtHeightIndex As Short
    
        Randomize(DateTime.Now.ToBinary)
        m_intR = Int(Rnd() * 255 + 1)
        m_intG = Int(Rnd() * 255 + 1)
        m_intB = Int(Rnd() * 255 + 1)
    
        m_dblColor1 = -0.05
        srtWidth = 1024
        srtHeight = 768
    
        dblCPRange = (m_sngCPMax - m_sngCPMin) / srtWidth
        dblCQRange = (m_sngCQMax - m_sngCQMin) / srtHeight
    
        For srtWidthIndex = m_srtWidthIndexStart To m_srtWidthIndexEnd
          For srtHeightIndex = 0 To srtHeight
    
            m_dblZX = m_sngCPMin + srtWidthIndex * dblCPRange
            m_dblZY = m_sngCQMin + srtHeightIndex * dblCQRange
            m_dblCPInitial = 0
            m_dblCQInitial = 0
            m_dblEscapeTime = 0
    
            Loop1()
    loop1:
            If m_dblRadius > m_intEscapeRadiusLimit Then
              m_dblColor2 = m_dblEscapeTime
              DrawMandelbrot(srtWidthIndex, srtHeightIndex)
            ElseIf m_dblEscapeTime < m_intEscapeTimeLimit Then
              m_dblColor2 = Abs(Log(m_dblRadius))
              Loop1()
              GoTo loop1
            Else
              m_dblColor2 = m_dblRadius
              DrawMandelbrot(srtWidthIndex, srtHeightIndex)
            End If
          Next
        Next
      End Sub
    
      Private Sub Loop1()
        Application.DoEvents()
    
        m_dblCP = m_dblCPInitial ^ 2 - m_dblCQInitial ^ 2 + m_dblZX
        m_dblCQ = 2 * m_dblCPInitial * m_dblCQInitial + m_dblZY
    
        m_dblEscapeTime = m_dblEscapeTime + 1
        m_dblRadius = Abs(m_dblCQ * m_dblCQ + m_dblCP * m_dblCP)
        m_dblCPInitial = m_dblCP
        m_dblCQInitial = m_dblCQ
      End Sub
    
      Private Sub DrawMandelbrot(ByVal WidthIndex As Short, ByVal HeightIndex As Short)
        Dim penDraw As New Pen(Color.Empty, 1)
    
        Dim grDraw As Graphics = m_picDraw.CreateGraphics
    
        m_intColorR = Abs(255 - Abs(Abs(m_intR - (2 * m_intG - 255) * _
                  (((Log(Abs(m_dblColor2 ^ 2 + 0.000001))))) * 2 ^ m_dblColor1) _
                  Mod 512 - 255))
        m_intColorG = Abs(255 - Abs(Abs(m_intG - (2 * m_intB - 255) * _
              (((Log((m_dblColor2 + 0.000001))))) * 2 ^ m_dblColor1) Mod 512 - 255))
        m_intColorB = Abs(255 - Abs(Abs(m_intB - (2 * m_intR - 255) * _
              (((Log(Abs(m_dblColor2 + 0.000001))))) * 2 ^ m_dblColor1) Mod 512 - 255))
    
        penDraw.Color = Color.FromArgb(m_intColorR, m_intColorG, m_intColorB)
        grDraw.DrawEllipse(penDraw, WidthIndex, HeightIndex, 1, 1)
        'g_grDraw.DrawEllipse(penDraw, WidthIndex, HeightIndex, 1, 1) '此处出错
      End Sub
    End Class
    
    Module modDeclare
      Public g_grDraw As Graphics
      Public g_bmpBuffer As Bitmap
    End Module

    2010年7月7日 4:15

答案

  • 感谢版主的提示,将代码做如下修改解决问题:

     SyncLock g_bmpBuffer
          g_grDraw.DrawEllipse(penDraw, WidthIndex, HeightIndex, 1, 1)
     End SyncLock
    

    2010年7月13日 14:08

全部回复

  • 你好!

         请问具体是什么错误?


    周雪峰
    2010年7月7日 7:57
    版主
  • 错误提示是这样的:

    如果在使用 GetHdc 方法后使用 Graphics 对象,请调用 ReleaseHdc 方法。

    未处理 System.InvalidOperationException
      Message=对象当前正在其他地方使用。
      Source=System.Drawing
      StackTrace:
           在 System.Drawing.Graphics.CheckErrorStatus(Int32 status)
           在 System.Drawing.Graphics.DrawEllipse(Pen pen, Int32 x, Int32 y, Int32 width, Int32 height)
           在 Fractal.Mandelbrot.DrawMandelbrot(Int16 WidthIndex, Int16 HeightIndex) 位置 c:\users\jobs\documents\visual studio 2010\Projects\Fractal\Fractal\frmMain.vb:行号 161
           在 Fractal.Mandelbrot.Draw() 位置 c:\users\jobs\documents\visual studio 2010\Projects\Fractal\Fractal\frmMain.vb:行号 121
           在 System.Threading.ExecutionContext.runTryCode(Object userData)
           在 System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
           在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
           在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
           在 System.Threading.ThreadHelper.ThreadStart()
      InnerException:

    2010年7月8日 13:29
  • 由于 PictureBox 控件绘图后,当有其他窗口覆盖该form或者该form最小化以后,PictureBox 上的图形就消失了,所以在 PictureBox 和内存中的 g_bmpBuffer 中同步绘图,绘图完毕后将 g_bmpBuffer 加载到 Picturebox:
    grDraw.DrawEllipse(penDraw, WidthIndex, HeightIndex, 1, 1) g_grDraw.DrawEllipse(penDraw, WidthIndex, HeightIndex, 1, 1) '此处出错
    在单线程下可以运行,但在多线程下出错

    2010年7月8日 13:48
  • 你好!

        对这段代码进行加锁来同步,看看能否解决!


    周雪峰
    2010年7月10日 5:59
    版主
  • 对ui线程的操作还是用invoke返回到ui线程实现
    http://feiyun0112.cnblogs.com/
    2010年7月13日 3:36
    版主
  • 感谢版主的提示,将代码做如下修改解决问题:

     SyncLock g_bmpBuffer
          g_grDraw.DrawEllipse(penDraw, WidthIndex, HeightIndex, 1, 1)
     End SyncLock
    

    2010年7月13日 14:08