0

その日に送信する必要のあるメールのリストを表示するウェブアプリがあります。ユーザーは送信する電子メールを選択し、ボタンをクリックしてそれらを生成できます。[送信]ボタンをクリックすると、電子メールを生成する別のスレッドでプロセスが開始され、一時フォルダーを削除してプロセスがクリーンアップされます。プロセスが終了すると、リピーターがリバウンドされてユーザーのビューが更新され、送信されたばかりの電子メールが削除されて、再度送信されないようになります。

私の問題は、2番目のスレッドからtempフォルダーを削除すると、UIが新しいRepeaterデータで更新されないことです。フォルダ自体ではなくフォルダ内のファイルを削除しただけで正しく更新され、2番目のスレッドではなく元のスレッドでフォルダの削除を実行した場合も正しく更新されます。

新しいスレッドコード

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

問題は、これが電子メールの送信が終了したサーバー上のバックグラウンドスレッドであるが、応答がすでにユーザーに送信されていることです。ユーザーがページを開くと、バックグラウンドスレッドが開始され、サーバーが親スレッドに応答を返しますが、応答が送信された後もしばらくの間、バックグラウンドスレッドはサーバー上で実行され続けます。

必要なことを実現するには、Javascriptまたはいくつかのajaxでページを更新してサーバーをポーリングし、このバックグラウンドスレッドが完了したかどうかを確認する必要があります。つまり、バックグラウンドスレッドが開始され、電子メールが処理中であることがユーザーに通知されます。2秒後、ページは自動的に更新され、ユーザーには電子メールが処理中であることが通知されます。最後に、すべての電子メールが送信されると、リピーターが更新され、自動更新が無効になります

于 2011-08-08T15:32:23.693 に答える
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 に答える