我有一个使用线程的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>
答案 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 )
那就是说,这仍然是解决问题的可怕方法。我只提供这个答案,以便您能够理解原始的潜在问题。