Multithreading in ASP.NET app???

J

JV

My ASP.NET application needs to accept data from a post and return quickly.
I've added a worker thread to wake up and save data asynchronously after the
data is received. But, it appears to me that IIS kills the process and
thus the worker thread right after my main ASP.NET thread returns (anyone
know for certain?).

Do you know of a design pattern to accomplish this?
 
N

Nicholas Paldino [.NET/C# MVP]

JV,

You have to figure out for what reason ASP.NET is killing the process.
ASP.NET does have application recycling if the app goes down, and sometimes,
if memory consumption is too high, it will recycle the process.

What I would recommend is writing a service that will perform this
operation for you, then using a technology like MSMQ (through
System.Messaging) to send a message to the service, which will then handle
the updating of the data.

Hope this helps.
 
O

Oscar Thornell

Hi,

Check this out...

/Oscar

//Buisness layer...gets called from an ASP.NET web page...
public bool InitAsyncOperation(string someData)
{
try
{
//Access layer
Access access = Access.GetInstance(CurrentUser);

if(access.IsRunning)
{
return false;
}
else
{
//First we do some sync operation...
Wrapper wrapper = access.Validate(someData);

//Then we kick of the async...
MyDelegate d = new MyDelegate(<Another method...that performs the async
operation...>);
AsyncHelper.Execute(d, wrapper);
}
}
catch(Exception ex)
{
if(ExceptionPolicy.HandleException(ex, CONTROLLER_EXCEPTION_POLICY))
throw;
}

return true;
}




///Class AsyncHelper
using System;
using System.Reflection;
using System.Threading;

namespace <SomeCompany>.<SomeApp>.Util
{
/// <summary>
/// Starting with the 1.1 release of the .NET Framework, the SDK docs
/// now carry a caution that mandates calling EndInvoke on delegates
/// you've called BeginInvoke on in order to avoid potential leaks.
/// This means you cannot simply "fire-and-forget" a call to BeginInvoke
/// when spawning a new worker thread from the thread pool without the
risk
/// of creating a memory leak.
///
/// The usage model is that instead of calling BeginInvoke against a
delegate,
/// you would instead call AsyncHelper.Execute, passing that delegate and
it's parameters as input.
/// See: http://staff.develop.com/woodring for further information.
/// </summary>
/// <example>
/// delegate void CalcAndDisplaySumDelegate( int a, int b );
/// CalcAndDisplaySumDelegate d = new
CalcAndDisplaySumDelegate(someCalc.Add);
/// AsyncHelper.Execute(d, 2, 3);
/// </example>
public class AsyncHelper
{
private static WaitCallback callback = new
WaitCallback(DynamicInvokeShim);

/// <summary>
/// Takes a delegate and a list of arguments. Performs asynchronous
invocation of the
/// target(the Class.Method the delegates points to..).
/// </summary>
/// <param name="d"></param>
/// <param name="args"></param>
/// <Author>Oscar Thornell</Author>
public static void Execute(Delegate d, params object[] args)
{
ThreadPool.QueueUserWorkItem(callback, new TargetInfo(d, args));
}

private static void DynamicInvokeShim(object obj)
{
TargetInfo targetInfo = (TargetInfo)obj;
targetInfo.Target.DynamicInvoke(targetInfo.Args);
}

class TargetInfo
{
internal readonly Delegate Target;
internal readonly object[] Args;

internal TargetInfo(Delegate d, object[] args)
{
Target = d;
Args = args;
}
}
}
}
 
E

Eliyahu Goldin

As you observed, you are not gaining much with multithreading in Asp.Net.

You can make a Windows service that will pick up requests made by the
Asp.Net application. The requests can be passed on in a number of ways. Two
most common ones are via a database and via files.

Eliyahu
 
N

Nicholas Paldino [.NET/C# MVP]

I wouldn't recommend using a database or files for notifications to the
service. That would require you to poll for the result, which on a general
level, is not too efficient.
 
E

Eliyahu Goldin

System.IO.FileSystemWatcher is a nice class that takes care of notification
in case of files.

Eliyahu

Nicholas Paldino said:
I wouldn't recommend using a database or files for notifications to the
service. That would require you to poll for the result, which on a
general level, is not too efficient.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Eliyahu Goldin said:
As you observed, you are not gaining much with multithreading in Asp.Net.

You can make a Windows service that will pick up requests made by the
Asp.Net application. The requests can be passed on in a number of ways.
Two most common ones are via a database and via files.

Eliyahu
 
J

Jon Skeet [C# MVP]

Eliyahu said:
System.IO.FileSystemWatcher is a nice class that takes care of notification
in case of files.

Not reliably, in my experience. I don't know *exactly* what goes wrong,
but often FileSystemWatcher can end up missing events, or waiting a
while and then delivering batches etc. I'm not the only one to have
noticed this. I still use FSW, but only in conjunction with something
which manually polls in case something goes wrong. It's all a bit of a
hack :(

Jon
 
J

JV

I've only skimmed over your code thus far, but it appears you are spawning a
thread from the threadpool for each ASP.NET request? If I'm correct about
that, that would become a scalability issue.
My goal is to have a single worker thread that manages the dataset and
multiple requests putting/getting data. Wish I could have just used MSSQL
for this.


Oscar Thornell said:
Hi,

Check this out...

/Oscar

//Buisness layer...gets called from an ASP.NET web page...
public bool InitAsyncOperation(string someData)
{
try
{
//Access layer
Access access = Access.GetInstance(CurrentUser);

if(access.IsRunning)
{
return false;
}
else
{
//First we do some sync operation...
Wrapper wrapper = access.Validate(someData);

//Then we kick of the async...
MyDelegate d = new MyDelegate(<Another method...that performs the
async operation...>);
AsyncHelper.Execute(d, wrapper);
}
}
catch(Exception ex)
{
if(ExceptionPolicy.HandleException(ex, CONTROLLER_EXCEPTION_POLICY))
throw;
}

return true;
}




///Class AsyncHelper
using System;
using System.Reflection;
using System.Threading;

namespace <SomeCompany>.<SomeApp>.Util
{
/// <summary>
/// Starting with the 1.1 release of the .NET Framework, the SDK docs
/// now carry a caution that mandates calling EndInvoke on delegates
/// you've called BeginInvoke on in order to avoid potential leaks.
/// This means you cannot simply "fire-and-forget" a call to
BeginInvoke
/// when spawning a new worker thread from the thread pool without the
risk
/// of creating a memory leak.
///
/// The usage model is that instead of calling BeginInvoke against a
delegate,
/// you would instead call AsyncHelper.Execute, passing that delegate and
it's parameters as input.
/// See: http://staff.develop.com/woodring for further information.
/// </summary>
/// <example>
/// delegate void CalcAndDisplaySumDelegate( int a, int b );
/// CalcAndDisplaySumDelegate d = new
CalcAndDisplaySumDelegate(someCalc.Add);
/// AsyncHelper.Execute(d, 2, 3);
/// </example>
public class AsyncHelper
{
private static WaitCallback callback = new
WaitCallback(DynamicInvokeShim);

/// <summary>
/// Takes a delegate and a list of arguments. Performs asynchronous
invocation of the
/// target(the Class.Method the delegates points to..).
/// </summary>
/// <param name="d"></param>
/// <param name="args"></param>
/// <Author>Oscar Thornell</Author>
public static void Execute(Delegate d, params object[] args)
{
ThreadPool.QueueUserWorkItem(callback, new TargetInfo(d, args));
}

private static void DynamicInvokeShim(object obj)
{
TargetInfo targetInfo = (TargetInfo)obj;
targetInfo.Target.DynamicInvoke(targetInfo.Args);
}

class TargetInfo
{
internal readonly Delegate Target;
internal readonly object[] Args;

internal TargetInfo(Delegate d, object[] args)
{
Target = d;
Args = args;
}
}
}
}



JV said:
My ASP.NET application needs to accept data from a post and return
quickly. I've added a worker thread to wake up and save data
asynchronously after the data is received. But, it appears to me that
IIS kills the process and thus the worker thread right after my main
ASP.NET thread returns (anyone know for certain?).

Do you know of a design pattern to accomplish this?
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,


JV said:
My ASP.NET application needs to accept data from a post and return
quickly. I've added a worker thread to wake up and save data

TO wake up ?
How r u storing the thread reference? in Application ?
How r u running it?
Show some code for more details.

What if you create the thread at each request? This allow you to create one
thread per request and not having to worry about concurrency, the drawback
is that you may create lots of threads depending of how busy is your web
site.



cheers,
 
J

JV

Thanks for the reply!

Actually I was storing the reference to the object in Session, but I could
just as easily move it to Application. That would actually make more sense.

I am trying to avoid creating a thread per request. That sort of defeats
the purpose of returning quickly for scalability.
 
R

Russell

I create a worker thread from within an ASPX page as follows (code is
actually in a server control used in the page, so it is defined in its
own named assembly):

System.Threading.ThreadStart ts = new
System.Threading.ThreadStart(ProcessCleanupFiles);
System.Threading.Thread thread = new System.Threading.Thread(ts);
thread.Start();

The page returns in a timely fashion and the thread survives to do
cleanup. For testing I tried putting a 30 second sleep into
ProcessCleanupFiles to test that a) it did not slow my page return b)
it does execute after the delay. I have not explicitly tested for
memory leaks but based on my understanding, "thread" should get cleaned
up sometime after both the invoking method and ProcessCleanupFiles
complete.
 
C

Cor Ligthert [MVP]

JV,

What does your mainthread do in the meantime.

With the information you give now, do I not see any sense for
multithreading.

Moreover the only thing you achieve is that your processing time will be
take more time.

Just my thought,

Cor
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

How much time the DB operation takes?

Do not store it in application, if you do so ALL the sessions will try to
use it.

If the DB operation takes a long time using MSQS would be a good suggestion
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

Jon Skeet said:
Not reliably, in my experience. I don't know *exactly* what goes wrong,
but often FileSystemWatcher can end up missing events, or waiting a
while and then delivering batches etc. I'm not the only one to have
noticed this. I still use FSW, but only in conjunction with something
which manually polls in case something goes wrong. It's all a bit of a
hack :(

Totally agree with you in this, not only you may lose events ( not my case
so far) but you may get duplicated events for a single user action. I use a
lock while processing and deleting(or moving) files as a workaround.

Anyway files are not the best option for the OP's problem
 
G

Guest

JV,
I see no reason why you couldn't have a class in Application State that
would hold a DataSet whose table to which you would add your post data.
Whenever a new row is added (or when "x" number of rows had been added) the
class would perform an DataAdapter Update to your Sql server and clear out
the datatable to wait for more posts.
Peter
 
J

JV

The goal is to allow the main thread to append some data or request some
data from a dataset. But persistence would be managed on another thread so
as not to slow up the ASP.NET application unless necessary.

Idea was to use a ManualResetEvent to let the worker thread know there was
some work to do and a Mutex to serialize access to the data. Thus, if many
requests were coming in close together, the worker thread might have to wait
awhile to do persistence, but the ASP.NET application could carry on with
the in-memory data.

Why would you say that this increases processing time? Maybe in total, but
performance for the ASP.NET application should be increased, which is the
key.
 
J

John Bailo

The problem is your code is finishing before the worker thread
completes. You need a method to evaluate that the worker thread has
completed before allow the ASP.NET application to end.

I use this pattern

//put my process on a thread
results.Add(this.as400maketable(s,tableString));

//the threaded method
private IAsyncResult as400maketable(string line, string tablename)
{
AsyncCallback delCB = new AsyncCallback(this.AsyncCB);
IAsyncResult ar = null;
//does a DB2 database insert
ar = as400.BegininsertData(line, tablename, delCB, null);
return ar;
}

//callback method
void AsyncCB(IAsyncResult ar)
{
as400.EndinsertPolarData(ar);
}

//Then back in my main thread I check to see all my threads are done:
foreach(IAsyncResult ar in results)
ar.AsyncWaitHandle.WaitOne();
 
J

Jon Skeet [C# MVP]

John Bailo said:
The problem is your code is finishing before the worker thread
completes. You need a method to evaluate that the worker thread has
completed before allow the ASP.NET application to end.

No, the whole point is (as I understand it) to make the web page return
quickly, queueing the data for insert later as it will take some time.
Unfortunately the way that IIS recycles AppDomains etc can make this
somewhat flaky. Instead of starting separate threads and assuming
they'll continue, the OP will need to queue the data in a more robust
way (eg to the file system or MSMQ) before returning, possibly
processing it in a different process altogether.
 
C

Cor Ligthert [MVP]

JV,

See this

Page load
Start workerthread
Do some things in the mainthread
Let main thread sleep in a processing loop to see if the workerthread is
ready and if ready go on
(otherwise the page is sent back and this page goes out of scope)
Sent page back

What is your advantage in this, that really should in my opinion really be
hug if it is in an ASPNET application more than the processing needed for
the threads and the checking of those are ready(it is not your code however
what is needed)?

And the processor is of course not only used by this client.

I hope this helps,

Cor
 
O

Oscar Thornell

Thats correct! It works in my solution, since the use case is to start a
batch like process that doesn´t happen to often...and let the user proceed
to other tasks..
I think that you should try using the threadpool and test the performance...

/Oscar

JV said:
I've only skimmed over your code thus far, but it appears you are spawning
a thread from the threadpool for each ASP.NET request? If I'm correct
about that, that would become a scalability issue.
My goal is to have a single worker thread that manages the dataset and
multiple requests putting/getting data. Wish I could have just used
MSSQL for this.


Oscar Thornell said:
Hi,

Check this out...

/Oscar

//Buisness layer...gets called from an ASP.NET web page...
public bool InitAsyncOperation(string someData)
{
try
{
//Access layer
Access access = Access.GetInstance(CurrentUser);

if(access.IsRunning)
{
return false;
}
else
{
//First we do some sync operation...
Wrapper wrapper = access.Validate(someData);

//Then we kick of the async...
MyDelegate d = new MyDelegate(<Another method...that performs the
async operation...>);
AsyncHelper.Execute(d, wrapper);
}
}
catch(Exception ex)
{
if(ExceptionPolicy.HandleException(ex, CONTROLLER_EXCEPTION_POLICY))
throw;
}

return true;
}




///Class AsyncHelper
using System;
using System.Reflection;
using System.Threading;

namespace <SomeCompany>.<SomeApp>.Util
{
/// <summary>
/// Starting with the 1.1 release of the .NET Framework, the SDK docs
/// now carry a caution that mandates calling EndInvoke on delegates
/// you've called BeginInvoke on in order to avoid potential leaks.
/// This means you cannot simply "fire-and-forget" a call to
BeginInvoke
/// when spawning a new worker thread from the thread pool without the
risk
/// of creating a memory leak.
///
/// The usage model is that instead of calling BeginInvoke against a
delegate,
/// you would instead call AsyncHelper.Execute, passing that delegate and
it's parameters as input.
/// See: http://staff.develop.com/woodring for further information.
/// </summary>
/// <example>
/// delegate void CalcAndDisplaySumDelegate( int a, int b );
/// CalcAndDisplaySumDelegate d = new
CalcAndDisplaySumDelegate(someCalc.Add);
/// AsyncHelper.Execute(d, 2, 3);
/// </example>
public class AsyncHelper
{
private static WaitCallback callback = new
WaitCallback(DynamicInvokeShim);

/// <summary>
/// Takes a delegate and a list of arguments. Performs asynchronous
invocation of the
/// target(the Class.Method the delegates points to..).
/// </summary>
/// <param name="d"></param>
/// <param name="args"></param>
/// <Author>Oscar Thornell</Author>
public static void Execute(Delegate d, params object[] args)
{
ThreadPool.QueueUserWorkItem(callback, new TargetInfo(d, args));
}

private static void DynamicInvokeShim(object obj)
{
TargetInfo targetInfo = (TargetInfo)obj;
targetInfo.Target.DynamicInvoke(targetInfo.Args);
}

class TargetInfo
{
internal readonly Delegate Target;
internal readonly object[] Args;

internal TargetInfo(Delegate d, object[] args)
{
Target = d;
Args = args;
}
}
}
}



JV said:
My ASP.NET application needs to accept data from a post and return
quickly. I've added a worker thread to wake up and save data
asynchronously after the data is received. But, it appears to me that
IIS kills the process and thus the worker thread right after my main
ASP.NET thread returns (anyone know for certain?).

Do you know of a design pattern to accomplish this?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top