How to access the Session object from a new thread ?

  • Thread starter Thread starter John Smith
  • Start date Start date
J

John Smith

As I understand it, a general rule of threading is that a new thread should
not access its owner's objects. Apparently this is why the Session object is
not available from a new thread created in an asp.net page. A generally
proposed solution involves passing a reference to the current HTTP context
as an argument into the new thread. The problem with this approach is that
this reference must then be passed to every function that the thread
calls... which could be many. But I do see a glimmer of hope...

While a new thread is running code within the parent asp.net page, the
following works in the new thread:

string x = (string)Session["MyVariableX"];

but the following does not work:

string x = (string)HttpContext.Current.Session["MyVariableX"];

In the latter case I get a "object reference not set" exception. So why does
the first case work and the 2nd doesn't? I would have thought that
Page.Session was equivalent to HttpContext.Current.Session, but obviously
not. What's the difference?
 
Where are you getting 'Session', is that the object you are passing in? If
so, this works because you are passing in the object reference to the
session, but in the other case you are trying to get the current http
context for the new thread, which doesn't exist, hence the null reference
exception.

You should try looking at the CallContext class, which is part of the
remoting libraries. It allows you to essentially hang objects on it, which
are then accessible anywhere on the thread. There is a GetData and SetData
method, which take and return objects, which you can then cast to the
appropriate type.
 
Hello John,
John Smith said:
As I understand it, a general rule of threading is that a new thread should
not access its owner's objects. Apparently this is why the Session object is
not available from a new thread created in an asp.net page. A generally
proposed solution involves passing a reference to the current HTTP context
as an argument into the new thread. The problem with this approach is that
this reference must then be passed to every function that the thread
calls... which could be many. But I do see a glimmer of hope...

While a new thread is running code within the parent asp.net page, the
following works in the new thread:

string x = (string)Session["MyVariableX"];

but the following does not work:

string x = (string)HttpContext.Current.Session["MyVariableX"];

In the latter case I get a "object reference not set" exception. So why does
the first case work and the 2nd doesn't? I would have thought that
Page.Session was equivalent to HttpContext.Current.Session, but obviously
not. What's the difference?

HttpContext context = HttpContext.Current;

string x = (string)context.Current.Session["MyVariableX"];

mfg simon g.
 
John Smith said:
As I understand it, a general rule of threading is that a new thread should
not access its owner's objects.

I trust that your thread doesn't reference the page after the page has
finished executing?
 
John Saunders said:
I trust that your thread doesn't reference the page after the page has
finished executing?

No, but you raise an interesting issue. The thread does try to reference the
current HttpContext when the page has finished executing. Is the HttpContext
kaput at that point? If so, I assume I must access the session some other
way, if at all.
 
Marina said:
Where are you getting 'Session', is that the object you are passing in? If
so, this works because you are passing in the object reference to the
session, but in the other case you are trying to get the current http
context for the new thread, which doesn't exist, hence the null reference
exception.

No, "Session" as in "this.Session" in the page class.
You should try looking at the CallContext class, which is part of the
remoting libraries. It allows you to essentially hang objects on it, which
are then accessible anywhere on the thread. There is a GetData and SetData
method, which take and return objects, which you can then cast to the
appropriate type.

I'll have a look at that. Thanks!
John Smith said:
As I understand it, a general rule of threading is that a new thread should
not access its owner's objects. Apparently this is why the Session
object
is
not available from a new thread created in an asp.net page. A generally
proposed solution involves passing a reference to the current HTTP context
as an argument into the new thread. The problem with this approach is that
this reference must then be passed to every function that the thread
calls... which could be many. But I do see a glimmer of hope...

While a new thread is running code within the parent asp.net page, the
following works in the new thread:

string x = (string)Session["MyVariableX"];

but the following does not work:

string x = (string)HttpContext.Current.Session["MyVariableX"];

In the latter case I get a "object reference not set" exception. So why does
the first case work and the 2nd doesn't? I would have thought that
Page.Session was equivalent to HttpContext.Current.Session, but obviously
not. What's the difference?
 
Gandalf said:
No, but you raise an interesting issue. The thread does try to reference the
current HttpContext when the page has finished executing. Is the HttpContext
kaput at that point? If so, I assume I must access the session some other
way, if at all.

I don't believe it's valid to referene the Session, or anything else related
to the page, after the page has been Disposed. The exceptions would be a
case where the Page creates some object and then passes it to the thread.

Does your thread change Session, or does it perhaps only change something
which is referred to by Session? For example, does your thread do:

Session["index"] = value;

or

MyClass mc = (MyClass) Session["index"];
mc.Property = value;

? The latter makes some sense, while the former does not. How does your
thread know that Session doesn't expire after the page has completed and
while it's still using it?

If you're doing the latter, why not pass (MyClass) Session["index"] to your
thread instead of having the thread reference Session?

Be very careful with threads in ASP.NET. Often, the kind of thing we would
do in a Windows application doesn't make sense in light of the ASP.NET page
model.
 
John Saunders said:
Gandalf said:
No, but you raise an interesting issue. The thread does try to reference the
current HttpContext when the page has finished executing. Is the HttpContext
kaput at that point? If so, I assume I must access the session some other
way, if at all.

I don't believe it's valid to referene the Session, or anything else related
to the page, after the page has been Disposed. The exceptions would be a
case where the Page creates some object and then passes it to the thread.

Does your thread change Session, or does it perhaps only change something
which is referred to by Session? For example, does your thread do:

Session["index"] = value;

or

MyClass mc = (MyClass) Session["index"];
mc.Property = value;

? The latter makes some sense, while the former does not. How does your
thread know that Session doesn't expire after the page has completed and
while it's still using it?

If you're doing the latter, why not pass (MyClass) Session["index"] to your
thread instead of having the thread reference Session?

Be very careful with threads in ASP.NET. Often, the kind of thing we would
do in a Windows application doesn't make sense in light of the ASP.NET page
model.

Thanks for your help, John. I have done some work on this over the past
week. The session IS available in the child thread, even after the spawning
page has been emitted. When calling the thread I also passed a reference to
the session using the following technique:

Thread MyThread = MyThreading.CreateThread
(new MyThreading.MethodDelegate(MyAsyncMethod),
HttpContext.Current.Session);
MyThread.Start();

public class MyThreading
{
/// <summary>
/// Pass the name of the method that the thread is to run
/// </summary>
public delegate void MethodDelegate(object method);

/// <summary>
/// This class serves to wrap a method with arguments as a method
/// without arguments, suitable for running in a new thread.
/// </summary>
private class ThreadWork
{
public object TheMethodArgs;
public MethodDelegate TheMethodDelegate;
public void Work()
{
TheMethodDelegate(TheMethodArgs);
}
}

/// <summary>
/// Creates a thread based on a method delegate and the method args.
/// The method should be defined as MyMethod(object args).
/// </summary>
/// <param name="methodDelegate">A new MyThreading.MethodDelegate for the
method to run</param>
/// <param name="methodArgs">An object holding all the arguments to be
passed to the method to run</param>
/// <returns>a new System.Threading.Thread which can then be
started</returns>
public static System.Threading.Thread CreateThread(MethodDelegate
methodDelegate, Object methodArgs)
{
ThreadWork NewThreadWork = new ThreadWork();
NewThreadWork.TheMethodArgs = methodArgs;
NewThreadWork.TheMethodDelegate = methodDelegate;
System.Threading.Thread NewThread = new System.Threading.Thread
(new System.Threading.ThreadStart(NewThreadWork.Work));
return NewThread;
}
}
 
John Smith said:
John Saunders said:
Gandalf said:
"John Saunders" <john.saunders at SurfControl.com> wrote in message
As I understand it, a general rule of threading is that a new thread
should
not access its owner's objects.

I trust that your thread doesn't reference the page after the page has
finished executing?

No, but you raise an interesting issue. The thread does try to
reference
the
current HttpContext when the page has finished executing. Is the HttpContext
kaput at that point? If so, I assume I must access the session some other
way, if at all.

I don't believe it's valid to referene the Session, or anything else related
to the page, after the page has been Disposed. The exceptions would be a
case where the Page creates some object and then passes it to the thread.

Does your thread change Session, or does it perhaps only change something
which is referred to by Session? For example, does your thread do:

Session["index"] = value;

or

MyClass mc = (MyClass) Session["index"];
mc.Property = value;

? The latter makes some sense, while the former does not. How does your
thread know that Session doesn't expire after the page has completed and
while it's still using it?

If you're doing the latter, why not pass (MyClass) Session["index"] to your
thread instead of having the thread reference Session?

Be very careful with threads in ASP.NET. Often, the kind of thing we would
do in a Windows application doesn't make sense in light of the ASP.NET page
model.

Thanks for your help, John. I have done some work on this over the past
week. The session IS available in the child thread, even after the spawning
page has been emitted. When calling the thread I also passed a reference to
the session using the following technique:

Thread MyThread = MyThreading.CreateThread
(new MyThreading.MethodDelegate(MyAsyncMethod),
HttpContext.Current.Session);
MyThread.Start();

public class MyThreading
{
/// <summary>
/// Pass the name of the method that the thread is to run
/// </summary>
public delegate void MethodDelegate(object method);

/// <summary>
/// This class serves to wrap a method with arguments as a method
/// without arguments, suitable for running in a new thread.
/// </summary>
private class ThreadWork
{
public object TheMethodArgs;
public MethodDelegate TheMethodDelegate;
public void Work()
{
TheMethodDelegate(TheMethodArgs);
}
}

/// <summary>
/// Creates a thread based on a method delegate and the method args.
/// The method should be defined as MyMethod(object args).
/// </summary>
/// <param name="methodDelegate">A new MyThreading.MethodDelegate for the
method to run</param>
/// <param name="methodArgs">An object holding all the arguments to be
passed to the method to run</param>
/// <returns>a new System.Threading.Thread which can then be
started</returns>
public static System.Threading.Thread CreateThread(MethodDelegate
methodDelegate, Object methodArgs)
{
ThreadWork NewThreadWork = new ThreadWork();
NewThreadWork.TheMethodArgs = methodArgs;
NewThreadWork.TheMethodDelegate = methodDelegate;
System.Threading.Thread NewThread = new System.Threading.Thread
(new System.Threading.ThreadStart(NewThreadWork.Work));
return NewThread;
}
}

Ok, so you've now determined that this "works", and I'm sure that you're
right. It will work right up until the time that it stops working.

If you haven't seen any examples of this from Microsoft, then you should
assume that it isn't meant to "work forever" until or unless you hear
otherwise from Microsoft.

Consider: if you pass a reference to Session into your thread, then Session
will certainly not be garbage-collected until your thread removes its
reference to it. But are you sure that Session will not be Disposed? Or that
ASP.NET (which created Session and could be said to own it) will not do
anything nasty to it? Keep in mind that ASP.NET is unaware of the existance
of your thread and has no reason to take your thread into account.

You want to be passing things to your thread which are owned by you, and
which you can control. For instance, if you currently have five variables in
Session, say Session["a"] through Session["e"], then I suggest that you
create a class with the five values in it:

public class ForSession
{
public object a;
public int b;
public int c;
public object d;
public string e;
public ForSession(object A, int B, int C, object D, string e) {...}
}

And put an instance of that class into Session:

Session["ForSession"] = new ForSession(...);

Then, pass that to your thread:

Thread MyThread = MyThreading.CreateThread(new
MyThreading.MethodDelegate(MyAsyncMethod), (ForSession)
Session["ForSession"]);

You would own this object entirely, and nothing that ASP.NET could decide to
do would affect it.
 
John Saunders said:
John Smith said:
John Saunders said:
"John Saunders" <john.saunders at SurfControl.com> wrote in message
As I understand it, a general rule of threading is that a new thread
should
not access its owner's objects.

I trust that your thread doesn't reference the page after the page has
finished executing?

No, but you raise an interesting issue. The thread does try to reference
the
current HttpContext when the page has finished executing. Is the
HttpContext
kaput at that point? If so, I assume I must access the session some other
way, if at all.

I don't believe it's valid to referene the Session, or anything else related
to the page, after the page has been Disposed. The exceptions would be a
case where the Page creates some object and then passes it to the thread.

Does your thread change Session, or does it perhaps only change something
which is referred to by Session? For example, does your thread do:

Session["index"] = value;

or

MyClass mc = (MyClass) Session["index"];
mc.Property = value;

? The latter makes some sense, while the former does not. How does your
thread know that Session doesn't expire after the page has completed and
while it's still using it?

If you're doing the latter, why not pass (MyClass) Session["index"] to your
thread instead of having the thread reference Session?

Be very careful with threads in ASP.NET. Often, the kind of thing we would
do in a Windows application doesn't make sense in light of the ASP.NET page
model.

Thanks for your help, John. I have done some work on this over the past
week. The session IS available in the child thread, even after the spawning
page has been emitted. When calling the thread I also passed a reference to
the session using the following technique:

Thread MyThread = MyThreading.CreateThread
(new MyThreading.MethodDelegate(MyAsyncMethod),
HttpContext.Current.Session);
MyThread.Start();

public class MyThreading
{
/// <summary>
/// Pass the name of the method that the thread is to run
/// </summary>
public delegate void MethodDelegate(object method);

/// <summary>
/// This class serves to wrap a method with arguments as a method
/// without arguments, suitable for running in a new thread.
/// </summary>
private class ThreadWork
{
public object TheMethodArgs;
public MethodDelegate TheMethodDelegate;
public void Work()
{
TheMethodDelegate(TheMethodArgs);
}
}

/// <summary>
/// Creates a thread based on a method delegate and the method args.
/// The method should be defined as MyMethod(object args).
/// </summary>
/// <param name="methodDelegate">A new MyThreading.MethodDelegate for the
method to run</param>
/// <param name="methodArgs">An object holding all the arguments to be
passed to the method to run</param>
/// <returns>a new System.Threading.Thread which can then be
started</returns>
public static System.Threading.Thread CreateThread(MethodDelegate
methodDelegate, Object methodArgs)
{
ThreadWork NewThreadWork = new ThreadWork();
NewThreadWork.TheMethodArgs = methodArgs;
NewThreadWork.TheMethodDelegate = methodDelegate;
System.Threading.Thread NewThread = new System.Threading.Thread
(new System.Threading.ThreadStart(NewThreadWork.Work));
return NewThread;
}
}

Ok, so you've now determined that this "works", and I'm sure that you're
right. It will work right up until the time that it stops working.

If you haven't seen any examples of this from Microsoft, then you should
assume that it isn't meant to "work forever" until or unless you hear
otherwise from Microsoft.

Consider: if you pass a reference to Session into your thread, then Session
will certainly not be garbage-collected until your thread removes its
reference to it. But are you sure that Session will not be Disposed? Or that
ASP.NET (which created Session and could be said to own it) will not do
anything nasty to it? Keep in mind that ASP.NET is unaware of the existance
of your thread and has no reason to take your thread into account.

You want to be passing things to your thread which are owned by you, and
which you can control. For instance, if you currently have five variables in
Session, say Session["a"] through Session["e"], then I suggest that you
create a class with the five values in it:

public class ForSession
{
public object a;
public int b;
public int c;
public object d;
public string e;
public ForSession(object A, int B, int C, object D, string e) {...}
}

And put an instance of that class into Session:

Session["ForSession"] = new ForSession(...);

Then, pass that to your thread:

Thread MyThread = MyThreading.CreateThread(new
MyThreading.MethodDelegate(MyAsyncMethod), (ForSession)
Session["ForSession"]);

You would own this object entirely, and nothing that ASP.NET could decide to
do would affect it.

Interesting. But the purpose of my "ForSession" object is to maintain
status/progress information across postbacks. If the session dies then the
issue of the object dying with it is moot. Another issue that comes to mind
is, how does the object inside the thread get synched with the same object
as referenced thru the session from another thread (postback)? What if I was
using a database session server, as opposed to in-proc? How could that work?

I'm starting to feel that the only reliable way to do this is to persist my
ForSession object to a database between reads/updates.
 
John Smith said:
John Saunders said:
be
a
case where the Page creates some object and then passes it to the thread.

Does your thread change Session, or does it perhaps only change something
which is referred to by Session? For example, does your thread do:

Session["index"] = value;

or

MyClass mc = (MyClass) Session["index"];
mc.Property = value;

? The latter makes some sense, while the former does not. How does your
thread know that Session doesn't expire after the page has completed and
while it's still using it?

If you're doing the latter, why not pass (MyClass) Session["index"] to
your
thread instead of having the thread reference Session?

Be very careful with threads in ASP.NET. Often, the kind of thing we would
do in a Windows application doesn't make sense in light of the ASP.NET
page
model.

Thanks for your help, John. I have done some work on this over the past
week. The session IS available in the child thread, even after the spawning
page has been emitted. When calling the thread I also passed a
reference
to
the session using the following technique:

Thread MyThread = MyThreading.CreateThread
(new MyThreading.MethodDelegate(MyAsyncMethod),
HttpContext.Current.Session);
MyThread.Start();

public class MyThreading
{
/// <summary>
/// Pass the name of the method that the thread is to run
/// </summary>
public delegate void MethodDelegate(object method);

/// <summary>
/// This class serves to wrap a method with arguments as a method
/// without arguments, suitable for running in a new thread.
/// </summary>
private class ThreadWork
{
public object TheMethodArgs;
public MethodDelegate TheMethodDelegate;
public void Work()
{
TheMethodDelegate(TheMethodArgs);
}
}

/// <summary>
/// Creates a thread based on a method delegate and the method args.
/// The method should be defined as MyMethod(object args).
/// </summary>
/// <param name="methodDelegate">A new MyThreading.MethodDelegate for the
method to run</param>
/// <param name="methodArgs">An object holding all the arguments to be
passed to the method to run</param>
/// <returns>a new System.Threading.Thread which can then be
started</returns>
public static System.Threading.Thread CreateThread(MethodDelegate
methodDelegate, Object methodArgs)
{
ThreadWork NewThreadWork = new ThreadWork();
NewThreadWork.TheMethodArgs = methodArgs;
NewThreadWork.TheMethodDelegate = methodDelegate;
System.Threading.Thread NewThread = new System.Threading.Thread
(new System.Threading.ThreadStart(NewThreadWork.Work));
return NewThread;
}
}

Ok, so you've now determined that this "works", and I'm sure that you're
right. It will work right up until the time that it stops working.

If you haven't seen any examples of this from Microsoft, then you should
assume that it isn't meant to "work forever" until or unless you hear
otherwise from Microsoft.

Consider: if you pass a reference to Session into your thread, then Session
will certainly not be garbage-collected until your thread removes its
reference to it. But are you sure that Session will not be Disposed? Or that
ASP.NET (which created Session and could be said to own it) will not do
anything nasty to it? Keep in mind that ASP.NET is unaware of the existance
of your thread and has no reason to take your thread into account.

You want to be passing things to your thread which are owned by you, and
which you can control. For instance, if you currently have five
variables
in
Session, say Session["a"] through Session["e"], then I suggest that you
create a class with the five values in it:

public class ForSession
{
public object a;
public int b;
public int c;
public object d;
public string e;
public ForSession(object A, int B, int C, object D, string e) {...}
}

And put an instance of that class into Session:

Session["ForSession"] = new ForSession(...);

Then, pass that to your thread:

Thread MyThread = MyThreading.CreateThread(new
MyThreading.MethodDelegate(MyAsyncMethod), (ForSession)
Session["ForSession"]);

You would own this object entirely, and nothing that ASP.NET could
decide
to
do would affect it.

Interesting. But the purpose of my "ForSession" object is to maintain
status/progress information across postbacks. If the session dies then the
issue of the object dying with it is moot.

Not exactly. My point is that your thread could potentially still be
executing when the session dies - ASP.NET won't exactly keep the session
around for the benefit of a thread it knows nothing about. Worst things
could even happen: ASP.NET could keep the session alive as far as aspx pages
are concerned, yet the references you're working with in your thread could
wind up being Disposed or worse.

In general, keep in mind that ASP.NET doesn't know anything at all about
your thread. Your thread is using something which came from ASP.NET without
its permission. ASP.NET is free to take your toy away or do anything else to
it that it likes, all without your knowledge. I'd be especially concerned
about things it might do to implement session persistence in SQL Server or
the state server.
Another issue that comes to mind
is, how does the object inside the thread get synched with the same object
as referenced thru the session from another thread (postback)? What if I was
using a database session server, as opposed to in-proc? How could that
work?

As I said above, you've already got that same problem. You think that your
thread is processing "the same session object as the page which started it",
but the page is gone now! ASP.NET will guarantee that the next page
requested by the "same user" within a reasonable amount of time will see the
contents of Session as they were at the end of the request which started
your thread. But there is no reason to believe that your thread, which is
not a page, will be able to do anything sensible with Session.
I'm starting to feel that the only reliable way to do this is to persist my
ForSession object to a database between reads/updates.

Whatever you do, make sure you do something "on your own". Do not depend on
accidents of ASP.NET implementation, especially not where multiple threads
are involved. In my opinion, you shouldn't even be using another thread
spawned from an ASP.NET page for the simple reason that ASP.NET shouldn't
have to take your thread into account. As a simple example, ASP.NET will
restart the current AppDomain if web.config changes. It won't wait for your
thread to finish before doing so, neither will it warn your thread ahead of
time!

ASP.NET doesn't really "do" threading. It does "do" multiple simultaneous
HTTP requests. You can expect the latter to work, but you should have no
expectation that the former will continue to work past the next patch you
apply to your server.
 
John Saunders said:
would
be
a
case where the Page creates some object and then passes it to the
thread.

Does your thread change Session, or does it perhaps only change
something
which is referred to by Session? For example, does your thread do:

Session["index"] = value;

or

MyClass mc = (MyClass) Session["index"];
mc.Property = value;

? The latter makes some sense, while the former does not. How does your
thread know that Session doesn't expire after the page has
completed
and
while it's still using it?

If you're doing the latter, why not pass (MyClass)
Session["index"]
to
your
thread instead of having the thread reference Session?

Be very careful with threads in ASP.NET. Often, the kind of thing we
would
do in a Windows application doesn't make sense in light of the ASP.NET
page
model.

Thanks for your help, John. I have done some work on this over the past
week. The session IS available in the child thread, even after the
spawning
page has been emitted. When calling the thread I also passed a reference
to
the session using the following technique:

Thread MyThread = MyThreading.CreateThread
(new MyThreading.MethodDelegate(MyAsyncMethod),
HttpContext.Current.Session);
MyThread.Start();

public class MyThreading
{
/// <summary>
/// Pass the name of the method that the thread is to run
/// </summary>
public delegate void MethodDelegate(object method);

/// <summary>
/// This class serves to wrap a method with arguments as a method
/// without arguments, suitable for running in a new thread.
/// </summary>
private class ThreadWork
{
public object TheMethodArgs;
public MethodDelegate TheMethodDelegate;
public void Work()
{
TheMethodDelegate(TheMethodArgs);
}
}

/// <summary>
/// Creates a thread based on a method delegate and the method args.
/// The method should be defined as MyMethod(object args).
/// </summary>
/// <param name="methodDelegate">A new MyThreading.MethodDelegate
for
the
method to run</param>
/// <param name="methodArgs">An object holding all the arguments to be
passed to the method to run</param>
/// <returns>a new System.Threading.Thread which can then be
started</returns>
public static System.Threading.Thread CreateThread(MethodDelegate
methodDelegate, Object methodArgs)
{
ThreadWork NewThreadWork = new ThreadWork();
NewThreadWork.TheMethodArgs = methodArgs;
NewThreadWork.TheMethodDelegate = methodDelegate;
System.Threading.Thread NewThread = new System.Threading.Thread
(new System.Threading.ThreadStart(NewThreadWork.Work));
return NewThread;
}
}

Ok, so you've now determined that this "works", and I'm sure that you're
right. It will work right up until the time that it stops working.

If you haven't seen any examples of this from Microsoft, then you should
assume that it isn't meant to "work forever" until or unless you hear
otherwise from Microsoft.

Consider: if you pass a reference to Session into your thread, then Session
will certainly not be garbage-collected until your thread removes its
reference to it. But are you sure that Session will not be Disposed?
Or
that
ASP.NET (which created Session and could be said to own it) will not do
anything nasty to it? Keep in mind that ASP.NET is unaware of the existance
of your thread and has no reason to take your thread into account.

You want to be passing things to your thread which are owned by you, and
which you can control. For instance, if you currently have five
variables
in
Session, say Session["a"] through Session["e"], then I suggest that you
create a class with the five values in it:

public class ForSession
{
public object a;
public int b;
public int c;
public object d;
public string e;
public ForSession(object A, int B, int C, object D, string e) {...}
}

And put an instance of that class into Session:

Session["ForSession"] = new ForSession(...);

Then, pass that to your thread:

Thread MyThread = MyThreading.CreateThread(new
MyThreading.MethodDelegate(MyAsyncMethod), (ForSession)
Session["ForSession"]);

You would own this object entirely, and nothing that ASP.NET could
decide
to
do would affect it.

Interesting. But the purpose of my "ForSession" object is to maintain
status/progress information across postbacks. If the session dies then the
issue of the object dying with it is moot.

Not exactly. My point is that your thread could potentially still be
executing when the session dies - ASP.NET won't exactly keep the session
around for the benefit of a thread it knows nothing about. Worst things
could even happen: ASP.NET could keep the session alive as far as aspx pages
are concerned, yet the references you're working with in your thread could
wind up being Disposed or worse.

In general, keep in mind that ASP.NET doesn't know anything at all about
your thread. Your thread is using something which came from ASP.NET without
its permission. ASP.NET is free to take your toy away or do anything else to
it that it likes, all without your knowledge. I'd be especially concerned
about things it might do to implement session persistence in SQL Server or
the state server.
Another issue that comes to mind
is, how does the object inside the thread get synched with the same object
as referenced thru the session from another thread (postback)? What if I was
using a database session server, as opposed to in-proc? How could that
work?

As I said above, you've already got that same problem. You think that your
thread is processing "the same session object as the page which started it",
but the page is gone now! ASP.NET will guarantee that the next page
requested by the "same user" within a reasonable amount of time will see the
contents of Session as they were at the end of the request which started
your thread. But there is no reason to believe that your thread, which is
not a page, will be able to do anything sensible with Session.
I'm starting to feel that the only reliable way to do this is to persist my
ForSession object to a database between reads/updates.

Whatever you do, make sure you do something "on your own". Do not depend on
accidents of ASP.NET implementation, especially not where multiple threads
are involved. In my opinion, you shouldn't even be using another thread
spawned from an ASP.NET page for the simple reason that ASP.NET shouldn't
have to take your thread into account. As a simple example, ASP.NET will
restart the current AppDomain if web.config changes. It won't wait for your
thread to finish before doing so, neither will it warn your thread ahead of
time!

ASP.NET doesn't really "do" threading. It does "do" multiple simultaneous
HTTP requests. You can expect the latter to work, but you should have no
expectation that the former will continue to work past the next patch you
apply to your server.

Thanks again John. Well I'm left with a very basic question then: how can I
implement lengthy (several minutes) processes in ASP.NET? Bear in mind that
this in an Intranet application and I can afford to have resources tied up
that long if needed. Up to now I've been been spawning a worker thread which
updates a process status object in session. Meanwhile the client browser
polls (using auto redirect) the ASP.NET server, showing a progress
indicator, until the process is finished. I'm currently working on moving
the status object into the database, but as you say, ASP.NET could still
kill the worker thread.
 
John Smith said:
Thanks again John. Well I'm left with a very basic question then: how can I
implement lengthy (several minutes) processes in ASP.NET? Bear in mind that
this in an Intranet application and I can afford to have resources tied up
that long if needed. Up to now I've been been spawning a worker thread which
updates a process status object in session. Meanwhile the client browser
polls (using auto redirect) the ASP.NET server, showing a progress
indicator, until the process is finished. I'm currently working on moving
the status object into the database, but as you say, ASP.NET could still
kill the worker thread.

John,

Not to be to Socratic here, but if we're concerned about ASP.NET killing a
worker thread spawned from an ASP.NET page, then perhaps our worker thread
should not be part of ASP.NET?

So, for instance, you could use remoting. Promote "ForSession" into a
first-class citizen with a name which reflects what it's about, then set it
up as a remote object. You'd be surprised how easy that is, at least when
you use the simplest remoting techniques, ignoring all those fancy issues
about using interfaces, SoapSuds, etc. Just:

LongProcess lp = new LongProcess(Session["a"], ... , Session["e"]); //
Or pass as params to the method
lp.BeginLongProcess(); // void LongProcess(), so no need to wait
for the results.

From here you can get a bit fancier. For instance, (and I haven't tried
this) to poll for completion, store the IAsyncResult from BeginLongProcess
in Session. Your poller page can then check for ((IAsyncResult)
Session["LongProcessAR"]).IsCompleted. You could even get results back for
your poller page to display:

IAsyncResult ar = (IAsyncResult) Session["LongProcessAR"];
if (ar.IsComplete)
{
LongProcess lp = (LongProcess) Session["LongProcess"];

int outParam1 = 0;
DataSet outParam2 = null;
string resultMessage = lp.EndLongProcess(ar, out outParam1, out
outParam2);
// From string LongProcess(out int outParam1, out DataSet
outParam2);

lblMessage.text = resultMessage;
lblHeader.Text = string.Format("{0} Rows Returned", outParam1);
grdLongProcessingResult.DataSource = outParam2;

DataBind();
}
 
John Saunders said:
John Smith said:
depend
on ahead
of

Thanks again John. Well I'm left with a very basic question then: how
can
I
implement lengthy (several minutes) processes in ASP.NET? Bear in mind that
this in an Intranet application and I can afford to have resources tied up
that long if needed. Up to now I've been been spawning a worker thread which
updates a process status object in session. Meanwhile the client browser
polls (using auto redirect) the ASP.NET server, showing a progress
indicator, until the process is finished. I'm currently working on moving
the status object into the database, but as you say, ASP.NET could still
kill the worker thread.

John,

Not to be to Socratic here, but if we're concerned about ASP.NET killing a
worker thread spawned from an ASP.NET page, then perhaps our worker thread
should not be part of ASP.NET?

So, for instance, you could use remoting. Promote "ForSession" into a
first-class citizen with a name which reflects what it's about, then set it
up as a remote object. You'd be surprised how easy that is, at least when
you use the simplest remoting techniques, ignoring all those fancy issues
about using interfaces, SoapSuds, etc. Just:

LongProcess lp = new LongProcess(Session["a"], ... , Session["e"]); //
Or pass as params to the method
lp.BeginLongProcess(); // void LongProcess(), so no need to wait
for the results.

From here you can get a bit fancier. For instance, (and I haven't tried
this) to poll for completion, store the IAsyncResult from BeginLongProcess
in Session. Your poller page can then check for ((IAsyncResult)
Session["LongProcessAR"]).IsCompleted. You could even get results back for
your poller page to display:

IAsyncResult ar = (IAsyncResult) Session["LongProcessAR"];
if (ar.IsComplete)
{
LongProcess lp = (LongProcess) Session["LongProcess"];

int outParam1 = 0;
DataSet outParam2 = null;
string resultMessage = lp.EndLongProcess(ar, out outParam1, out
outParam2);
// From string LongProcess(out int outParam1, out DataSet
outParam2);

lblMessage.text = resultMessage;
lblHeader.Text = string.Format("{0} Rows Returned", outParam1);
grdLongProcessingResult.DataSource = outParam2;

DataBind();
}

OK, here's my plan of attack.... I have created a DB table to hold process
status. I have created a Windows service with a remoting interface to run my
async process. From my ASP.NET page I kick off my process by calling to my
service. I then poll the status table on each client postback until I get
notified that the process has completed. I'm not using the sessionstate
since potentially I need the mechanism to operate across sessions (browser
close/open). By using a Windows service and a remote interface I avoid all
the threading problems in ASP.NET.

How's that? :-)
 
John Smith said:
have
no patch
you can
tied
up
that long if needed. Up to now I've been been spawning a worker thread which
updates a process status object in session. Meanwhile the client browser
polls (using auto redirect) the ASP.NET server, showing a progress
indicator, until the process is finished. I'm currently working on moving
the status object into the database, but as you say, ASP.NET could still
kill the worker thread.

John,

Not to be to Socratic here, but if we're concerned about ASP.NET killing a
worker thread spawned from an ASP.NET page, then perhaps our worker thread
should not be part of ASP.NET?

So, for instance, you could use remoting. Promote "ForSession" into a
first-class citizen with a name which reflects what it's about, then set it
up as a remote object. You'd be surprised how easy that is, at least when
you use the simplest remoting techniques, ignoring all those fancy issues
about using interfaces, SoapSuds, etc. Just:

LongProcess lp = new LongProcess(Session["a"], ... , Session["e"]); //
Or pass as params to the method
lp.BeginLongProcess(); // void LongProcess(), so no need to wait
for the results.

From here you can get a bit fancier. For instance, (and I haven't tried
this) to poll for completion, store the IAsyncResult from BeginLongProcess
in Session. Your poller page can then check for ((IAsyncResult)
Session["LongProcessAR"]).IsCompleted. You could even get results back for
your poller page to display:

IAsyncResult ar = (IAsyncResult) Session["LongProcessAR"];
if (ar.IsComplete)
{
LongProcess lp = (LongProcess) Session["LongProcess"];

int outParam1 = 0;
DataSet outParam2 = null;
string resultMessage = lp.EndLongProcess(ar, out outParam1, out
outParam2);
// From string LongProcess(out int outParam1, out DataSet
outParam2);

lblMessage.text = resultMessage;
lblHeader.Text = string.Format("{0} Rows Returned", outParam1);
grdLongProcessingResult.DataSource = outParam2;

DataBind();
}

OK, here's my plan of attack.... I have created a DB table to hold process
status. I have created a Windows service with a remoting interface to run my
async process. From my ASP.NET page I kick off my process by calling to my
service. I then poll the status table on each client postback until I get
notified that the process has completed. I'm not using the sessionstate
since potentially I need the mechanism to operate across sessions (browser
close/open). By using a Windows service and a remote interface I avoid all
the threading problems in ASP.NET.

How's that? :-)

That sounds great! It's probably pretty fast, especially if you poll the
status table by its primary key.

Let us know if it works out as well as it sounds!
 
John, I just posted the link quickly for the benefit of anyone looking
through the archives.

One thing it mentions is that threading is supported (in the suggested form)
under ASP.NET. I quote, "Asynchronous programming is a feature supported by
many areas of the .NET Framework, including:.. ASP.NET Web Forms". The
mechanism also allows for easier parameter passing into the new thread, and
for better exception handling. I'm not sure if it should be used to persist
an activity across page postbacks, but at least the new threads are taken
from the thread pool and ASP.NET knows about them.

Having converted our application to use a Windows service, I'm now wondering
if I should have just tweaked my original threading design to use async
delegates. The service design works well so I'm not too concerned as to the
answer.
 
Back
Top