0

我有一个网络应用程序,它显示了当天需要发送的电子邮件列表。用户可以选择要发送的电子邮件,然后单击一个按钮来生成它们。当他们单击“发送”按钮时,会在另一个生成电子邮件的线程上启动一个进程,然后通过删除临时文件夹自行清理。该过程完成后,Repeater 将重新启动以更新用户视图并删除刚刚发送的电子邮件,以免再次发送。

我的问题是,当我从第二个线程中删除临时文件夹时,UI 不会使用新的中继器数据进行更新。如果我只是删除文件夹中的文件而不是文件夹本身,它会正确更新,如果我在原始线程而不是第二个线程上运行删除文件夹,它也会正确更新。

新线程代码

Dim t as Thread = New Thread(New ThreadStart(AddressOf EmailLetters))
t.Start()

删除文件夹代码

Dim fs = Server.CreateObject("Scripting.FileSystemObject")
fs.DeleteFolder(Server.MapPath(".") + "\tmpEmailFiles")

当我删除另一个线程上的文件夹时,为什么 UI 不会更新以显示新的转发器值?

编辑

这是一些显示问题的示例代码。对不起,如果它有点乱,但我只需要一些简单的东西来帮助我识别问题。

当您单击该按钮时,将启动一个线程并开始执行一个 JavaScript 加载脚本,该脚本每 10 秒执行一次 PostBack。每个回发都会检查线程是否完整并更新显示结果的状态标签。如果我从后台线程中删除一个文件夹,则状态标签的最终更新永远不会发生。如果我删除 DeleteFolder 调用,它确实如此。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ Import Namespace="System.Threading" %>
<%@ Page Language="VB" Debug="true" %>
<%@ Implements Interface="System.Web.UI.IPostBackEventHandler" %>

<SCRIPT language="vb" runat="server">

  Sub Page_Load(Src As Object, e As EventArgs)

  End Sub

  Public Sub Test(src as Object, e as EventArgs)

    Dim t as Thread = New Thread(New ThreadStart(AddressOf TestWorker))
    t.Start()

    Session("BackgroundThread") = t

  End Sub

  Public Sub TestWorker()

    ' 30 Second Delay
    System.Threading.Thread.Sleep(30000)

    Dim root as String = Server.MapPath(".")
    Dim fs = Server.CreateObject("Scripting.FileSystemObject")
    If Not fs.FolderExists(root + "\Test") Then fs.CreateFolder(root + "\Test")
    fs.DeleteFolder(root + "\Test")

    ErrMsg.Text = "Start: " + DateTime.Now.ToString()

  End Sub

  Public Sub RaisePostBackEvent(ByVal eventArgument As String) _
    Implements IPostBackEventHandler.RaisePostBackEvent

    If Session("BackgroundThread") is Nothing Then
      Exit Sub
    End If

    Dim t as Thread = CType(Session("BackgroundThread"), Thread)
    If t.ThreadState = ThreadState.Stopped Then
      ErrMsg.Text = "Done: " + DateTime.Now.ToString() 
      Session("BackgroundThread") = Nothing
    Else
      ErrMsg.Text = "Processing: " + DateTime.Now.ToString() + " - " + t.ThreadState.ToString()
    End If

  End Sub

</SCRIPT>

<HTML>
<HEAD>
  <SCRIPT language="javascript">
  //<!--

  function onLoad()
  {
    if(<%= IIF(Session("BackgroundThread") is Nothing, "false", "true") %>)
    {
      toggleLoading();
      setTimeout("<%= Page.ClientScript.GetPostBackEventReference(Me, "") %>", 10000);
    }
  }

  function toggleLoading(){
    document.getElementById('imgLoading').style.display = 'block';
    setTimeout("document.images['imgLoading'].src='images/loading.gif'", 100); 
  }

  // -->
  </SCRIPT>
</HEAD>

<BODY OnLoad="onLoad();">
<FORM runat="server">

  <ASP:Button runat="server" Text="Test" OnClick="Test" onClientClick="javascript: toggleLoading();" />
  <ASP:Label runat="server" Id="ErrMsg" />
  <IMG id="imgLoading" src="images/loading.gif" style="display: none;" />

</FORM>
</BODY>
</HTML>
4

2 回答 2

1

想通了我的问题。原来删除 ASP 根文件夹中的任何文件夹都会重新启动应用程序并重置所有会话变量。

为了解决这个问题,我在我的Application_OnStart方法中添加了以下内容Global.asax

Dim p As System.Reflection.PropertyInfo = GetType(HttpRuntime).GetProperty("FileChangesMonitor", Reflection.BindingFlags.Public Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static)
Dim o As Object = p.GetValue(Nothing, Nothing)
Dim f As System.Reflection.FieldInfo = o.GetType.GetField("_dirMonSubdirs", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.IgnoreCase)
Dim monitor As Object = f.GetValue(o)
Dim m As System.Reflection.MethodInfo = monitor.GetType.GetMethod("StopMonitoring", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
m.Invoke(monitor, New Object() {})
于 2011-08-08T16:06:59.530 回答
1

问题是这是服务器上的后台线程,正在发送电子邮件完成,但响应已发送给用户。当用户打开页面时,后台线程启动,服务器返回父线程上的响应,后台线程仍然在服务器上运行一段时间后响应已经发送

要实现您想要的,您需要在 Javascript 或某些 ajax 上刷新页面以轮询服务器并继续检查此后台线程是否已完成。即后台线程启动,并告知用户正在处理电子邮件,2 秒后页面自动刷新,用户仍被告知正在处理电子邮件。最后,当所有电子邮件都发送完毕后,中继器会更新并禁用自动刷新

于 2011-08-08T15:32:23.693 回答