0

I am trying to spawn a new thread from an ASP page written in VB.NET, on .NET 3.5.

The page allows a user to upload files and then processes the files into the database. As the processing can take a while, I want to upload them, spawn the processing onto a new thread, and then when it completes send the user a notification through our database-driven notification module in the system.

I've tried a couple different examples I've found online, but they don't seem to work. There is no error thrown, but it never seems to actually launch the processing code on a new thread either.

Here is my submittal code in the web page:

Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSubmit.Click
    Try
        If Not (file1.HasFile Or file2.HasFile Or file3.HasFile Or file4.HasFile Or file5.HasFile) Then
            AddErrorMessage("Please specify at least one file.")
        Else
            Dim l = New List(Of InventoryUploads.File)

            If file1.HasFile Then l.Add(New InventoryUploads.File With {.Name = file1.FileName, .Content = file1.FileContent})
            If file2.HasFile Then l.Add(New InventoryUploads.File With {.Name = file2.FileName, .Content = file2.FileContent})
            If file3.HasFile Then l.Add(New InventoryUploads.File With {.Name = file3.FileName, .Content = file3.FileContent})
            If file4.HasFile Then l.Add(New InventoryUploads.File With {.Name = file4.FileName, .Content = file4.FileContent})
            If file5.HasFile Then l.Add(New InventoryUploads.File With {.Name = file5.FileName, .Content = file5.FileContent})

            InventoryUploads.ProcessFiles(l, Session.UserIdent, chkRcvOverwrite.Checked)

            NewRow("Your files have been queued and are being processed. You will be sent a notification when they are completed.")
        End If
    Catch ex As Exception
        LogEx(ex)
        NewRow("There was an error queueing your files." & BR & ex.Message)
    End Try
End Sub

From a UI point of view, this all works, the page posts the files, and it shows the user the message about "your files have been queued".

The call to InventoryUploads.ProcessFiles is an encapsulation function in front of the threading code, which is all contained in the InventoryUploads module (following):

Imports System.Threading
Imports System.Threading.Tasks

Public Module InventoryUploads
  Public Structure File
    Private pName As String
    Private pContent As IO.Stream
    Private pProcessed As Boolean

    Public Property Name As String
        Get
            Return pName
        End Get
        Set(value As String)
            pName = value
        End Set
    End Property

    Public Property Content As IO.Stream
        Get
            Return pContent
        End Get
        Set(value As IO.Stream)
            pContent = value
        End Set
    End Property

    Public Property Processed As Boolean 
        Get
            Return pProcessed
        End Get
        Set(value As Boolean)
            pProcessed = value
        End Set
    End Property
  End Structure

  Public Sub ProcessFiles(files As List(Of File), userident As String, receivingsOverwrite As Boolean)
    Try
        Dim params = Array.CreateInstance(GetType(Object), 3)
        params(0) = files
        params(1) = userident
        params(2) = receivingsOverwrite

        '// Threading Method 1
        Dim pts = New ParameterizedThreadStart(AddressOf ThreadedProcessFiles)
        Dim thd = New Thread(pts)
        thd.IsBackground = True
        thd.Start(params)

        '// Threading Method 2
        'ThreadPool.QueueUserWorkItem(AddressOf ThreadedProcessFiles, params)

        '// Threading Method 3
        Dim f As Func(Of Integer) = Function() ThreadedProcessFiles(params)
        Task.Factory.StartNew(f)
    Catch ex As Exception
        IO.File.WriteAllText("C:\idwherenet.log", ex.Message)
        Throw
    End Try
  End Sub

''' <summary>
''' 
''' </summary>
''' <param name="params">exepcts params to contain 3 objects, 
''' 1) type List(Of File), 
''' 2) string userident to notify on finish
''' 3) boolean whether receivings should overwrite</param>
''' <remarks></remarks>
  Private Function ThreadedProcessFiles(params() As Object) As Boolean
    IO.File.WriteAllText("C:\mylog.log", "hello ThreadedProcessFiles?")
    'Log("threadedprocessfiles: got here")

    Dim files As List(Of File), uident As String = "", rcvovr As Boolean
    Try
        files = params(0)
        uident = CStr(params(1))
        rcvovr = CBool(params(2))

        If files.Count = 0 Then
            SendNotification(NotificationTypes.SYS, uident, "No files provided for inventory upload; nothing processed.")
            Exit Sub
        End If

        For Each f In files
            f.Processed = False
            If f.Content.Length = 0 Then Continue For

            '// process the file here....

            f.Processed = True
        Next

        SendNotification(NotificationTypes.SYS, uident, "Inventory upload processing completed.")
        Return True
    Catch ex As Exception
        'LogEx(ex)
        'Log(ex.Message, ex.ToString)
        If Not uident.IsEmpty Then SendNotification(NotificationTypes.SYS, uident, "Inventory upload processing encountered an error. " & ex.Message, ex.ToString)
        Return False
    End Try
  End Sub
End Module

In the ProcessFiles sub, you can see the three different methods I have tried to spawn the thread, including a NuGet backport of the Task Parallel Library... neither works. I have confirmed that is is making it into the ProcessFiles sub, and the code to spawn the thread is not throwing any errors... it just never actually calls the ThreadedProcessFiles function.

Any ideas?

UPDATE: Oh, actually, the NuGet package DOES seem to be working, FINALLY! It just doesn't write out to the file (context/security/impersonation permissions I guess). Now I can finally move on!

eidylon
  • 7,068
  • 20
  • 75
  • 118

1 Answers1

3

If you're using .NET 4 or 4.5, the Task Parallel Library makes this sort of thing super easy:

Task.Factory.StartNew(Function() ThreadedProcessFiles(params));

(I think that's the right syntax for VB)

If you go this route, I would change the params to just send the three parameters separately.

Jason P
  • 26,984
  • 3
  • 31
  • 45
  • Oh, that does look enticing! Unfortunately this site is still on .NET 3.5 :( (*should've stated that, updating*). – eidylon May 17 '13 at 17:45
  • Ooo, after you mentioned TPL tho, I went searching on that... looks like someone [backported it to .NET 3.5](http://stackoverflow.com/questions/2987439/can-i-use-the-task-parallel-library-in-a-net-3-5-project) ... off to check that out! – eidylon May 17 '13 at 17:50
  • :( DANGIT! Same thing; installed the TPL NuGet package, coded it up, works fine, no errors, but STILL no run. *sigh* – eidylon May 17 '13 at 18:51
  • Thank you, this actually DID work... just whatever security context the TPL task runs under did not have rights to write to the file i was logging to (even tho the same line in the calling procedure worked fine, even tho the doc says it's supposed to run under the same context as the caller). Thanks SO much for turning me on to this! – eidylon May 17 '13 at 19:50