编译debug = false导致ASP.NET线程问题

时间:2014-07-17 13:16:54

标签: asp.net vb.net multithreading

我有一个使用线程的Web应用程序,当编译调试设置为true时运行正常,但是一旦设置为false,页面就会导致CPU出现峰值并且永远不会加载。

我创建了一个非常简化的代码版本,以便测试下面的这个问题。

编辑:请注意Do While JobFinished的目标< 1循环是为了防止页面加载直到所有线程都完成,所以我可以在线程中向用户显示计算结果。当编译调试设置为true时,这似乎不是问题。

非常感谢。

Imports System.Xml
Imports System.IO
Imports System.Data.OleDb
Imports System.Data.SqlClient
Imports System.Data
Imports System.Math
Imports System.Threading

Partial Class _Default

    Inherits System.Web.UI.Page

    Protected MyThread As Thread
    Protected WithEvents myRequest As engineThread
    Protected JobFinished As Integer = 0

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        ltMessage.Text = "<root>"

        myRequest = New engineThread()
        MyThread = New Thread(AddressOf myRequest.callEngine)
        MyThread.Start()

        Do While JobFinished < 1
        Loop

        ltMessage.Text &= "</root>"

    End Sub

    Public Sub engineHandler1() Handles myRequest.ThreadComplete

        ltMessage.Text &= "It Works!"
        JobFinished = JobFinished + 1

    End Sub

    Public Class engineThread

        Public Event ThreadComplete()

        Public Sub callEngine()

            RaiseEvent ThreadComplete()

        End Sub

    End Class

End Class

我的web.config简单如下

<configuration>
    <system.web>      
      <compilation debug="false" strict="false" explicit="true" targetFramework="4.0" />
      <httpRuntime targetFramework="4.0" executionTimeout="600" />
    </system.web>
</configuration>

4 个答案:

答案 0 :(得分:3)

此...

Do While JobFinished < 1
Loop

太可怕了。这意味着一个核心将无缘无故地全速旋转。

创建一个事件,以便在JobFinished计数发生变化时发出信号......

Dim DoneEvent As New AutoResetEvent(False)

Do While JobFinished < 1
    DoneEvent.WaitOne(1000)
Loop

然后是工人......

myRequest = New engineThread()
MyThread = New Thread(Sub() 
                          myRequest.callEngine()
                          DoneEvent.Set()
                      End Sub)
MyThread.Start()

此外,没有原因在这里使用线程 - 您正在创建一个新线程,使用它,等待它完成(效率低下)然后继续。既然你无论如何都要阻止原始线程,为什么不把它用于手头的工作呢?

答案 1 :(得分:2)

Do While JobFinished < 1
Loop

这不是一个好主意 - 这会浪费尽可能多的CPU。您可以等待线程以Thread.Join()方法结束。

至于问题的确切原因,我怀疑在发布模式下,你的threadComplete永远不会执行。用Join()替换该无限循环并删除threadcomplete事件,看看是否能解决您的问题。

顺便说一句,在线程之间发出信号的正确方法是ManualResetEvent或AutoResetEvent对象,而不是bool变量。

答案 2 :(得分:1)

JobFinished = JobFinished + 1
...
Do While JobFinished < 1
    Loop

这不是线程安全的代码。与编译设置无关。 JobFinishes必须为volatile(其中VB has no knowledge, so use MemoryBarier)且+1必须为interlocked。现在你的代码可能会错过更新并导致无限循环(这正是你所看到的)。

我可以保证无论你想做什么,都可以在没有线程的情况下完成。您发布的代码显示了理解MT的严重差距。 在你走路之前,你会被烧伤很多。你过了Multithreading in Visual Basic了吗?例如,您在此处显示的代码永远不应使用循环编写。毕竟,这正是事件的用途。阅读Advanced Synchronization Techniques

答案 3 :(得分:1)

其他海报提供了解决此问题的正确方法 - 使用page_load等锁定阻止AutoResetEvent

与问题的直接问题交谈:为什么关闭调试会破坏代码?当您关闭调试时,您可能会启用优化。

如编写的那样,循环最有可能优化为无限循环。循环条件变量永远不会在循环范围内被修改,并且由于没有努力使用任何类型的内存屏障,编译器完全有权期望该值永远不会改变。编译器可以自由地将该循环优化为无限循环,因为您依赖于未定义的行为。

具体来说,编译器可能会优化到如下所示的内容:

If JobFinished < 1 Then
     Do While True
     Loop
End If

或者在C#中:

if ( JobFinished < 1 ) {
     while(true) {}
}

至少,您应该将变量标记为volatile,但是,您无法在VB中执行此操作。

相反,使用强制执行内存屏障语义的函数 - Thread.VolatileRead()Thread.VolatileWrite()

page_load

Do While Thread.VolatileRead( ByRef JobFinished ) < 1 Then 
Loop

engineHandler1

Thread.VolatileWrite( ByRef JobFinished, JobFinished + 1 )

那就是说,这仍然是解决问题的可怕方法。我只提供这个答案,以便您能够理解原始的潜在问题。