threading and session object

M

MattB

Hello

I am starting a new thread in a button click event.

This thread calls an method which sends emails, I don't want the page
to wait for the emails to finish going out as it slows the user down.

I have to set a session to null in the same click method but when
I do this I get this funny error.

"An unhandled exception of type
'System.Runtime.Serialization.SerializationException' occurred in
Unknown Module.

Additional information: The type System.Web.HttpException in Assembly
System.Web, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a is not marked as serializable."

Here is a rough example of the code. There is no difference if I move
the Session=null
statement before or after the thread, or if I move the email code into
a separate class.

private void MultipleAttachEmail_Click(object sender, System.EventArgs
e)
{
ThreadStart ts = new ThreadStart(multipleMail);
Thread thread = new Thread(ts);
thread.Start();
Session["BasketSession"] = null;
Response.Redirect("DocBasketCheckout.aspx?%^="+EmailAddress.Text);
}

private void multipleMail ()
{
MailMessage msgMail = new MailMessage();
msgMail.To = EmailAddress.Text;
msgMail.From = "(e-mail address removed)";
msgMail.Subject = "Document Basket Contents";
msgMail.BodyFormat = MailFormat.Text;
msgMail.Body = " ";

// Gets document location path from Document Basket.
foreach(DataGridItem dgi in documentDataGrid.Items)
{
string dLink = baseURL + dgi.Cells[2].Text;
msgMail.Attachments.Add(new MailAttachment(Server.MapPath( dLink
)));
}
SmtpMail.SmtpServer = "127.0.0.1";
SmtpMail.Send(msgMail);
}
 
R

Rick Strahl [MVP]

Hi Matt,

This is not directly addressing your question, but I think it might very
well be related.

You'll want to be real careful with threads in this scenario. I think the
failure you're seeing might have nothing to do with clearing the session but
something that's failing inside that thread method (for example the DataGrid
access).

The problem here is that when you fire off a new thread and you point it at
the method in question the class that it belongs to or some of hte
components on it might no longer be there because the page and the class
that goes with it has terminated already. One thing you can do work around
this is a use a static method.

In your scenario I would probably build the message on the ASP.Net thread
(because it relies on some data from the page) and fire only the sending on
the new thread and stick that into a generic static method. IOW, minimize
the reliance of the thread method on anything from the main ASP.Net page
which may be terminated/ing when the thread executes.

+++ Rick ---

--

Rick Strahl
West Wind Technologies
http://www.west-wind.com/
http://www.west-wind.com/weblog/
----------------------------------
Making waves on the Web


MattB said:
Hello

I am starting a new thread in a button click event.

This thread calls an method which sends emails, I don't want the page
to wait for the emails to finish going out as it slows the user down.

I have to set a session to null in the same click method but when
I do this I get this funny error.

"An unhandled exception of type
'System.Runtime.Serialization.SerializationException' occurred in
Unknown Module.

Additional information: The type System.Web.HttpException in Assembly
System.Web, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a is not marked as serializable."

Here is a rough example of the code. There is no difference if I move
the Session=null
statement before or after the thread, or if I move the email code into
a separate class.

private void MultipleAttachEmail_Click(object sender, System.EventArgs
e)
{
ThreadStart ts = new ThreadStart(multipleMail);
Thread thread = new Thread(ts);
thread.Start();
Session["BasketSession"] = null;
Response.Redirect("DocBasketCheckout.aspx?%^="+EmailAddress.Text);
}

private void multipleMail ()
{
MailMessage msgMail = new MailMessage();
msgMail.To = EmailAddress.Text;
msgMail.From = "(e-mail address removed)";
msgMail.Subject = "Document Basket Contents";
msgMail.BodyFormat = MailFormat.Text;
msgMail.Body = " ";

// Gets document location path from Document Basket.
foreach(DataGridItem dgi in documentDataGrid.Items)
{
string dLink = baseURL + dgi.Cells[2].Text;
msgMail.Attachments.Add(new MailAttachment(Server.MapPath( dLink
)));
}
SmtpMail.SmtpServer = "127.0.0.1";
SmtpMail.Send(msgMail);
}
 
S

Sharon

Another possible way, is to store all data relevant to the thread operation,
in a value type, and pass it by value to the method.
Thus copying the relevant data.
Sharon.


Rick Strahl said:
Hi Matt,

This is not directly addressing your question, but I think it might very
well be related.

You'll want to be real careful with threads in this scenario. I think the
failure you're seeing might have nothing to do with clearing the session but
something that's failing inside that thread method (for example the DataGrid
access).

The problem here is that when you fire off a new thread and you point it at
the method in question the class that it belongs to or some of hte
components on it might no longer be there because the page and the class
that goes with it has terminated already. One thing you can do work around
this is a use a static method.

In your scenario I would probably build the message on the ASP.Net thread
(because it relies on some data from the page) and fire only the sending on
the new thread and stick that into a generic static method. IOW, minimize
the reliance of the thread method on anything from the main ASP.Net page
which may be terminated/ing when the thread executes.

+++ Rick ---

--

Rick Strahl
West Wind Technologies
http://www.west-wind.com/
http://www.west-wind.com/weblog/
----------------------------------
Making waves on the Web


MattB said:
Hello

I am starting a new thread in a button click event.

This thread calls an method which sends emails, I don't want the page
to wait for the emails to finish going out as it slows the user down.

I have to set a session to null in the same click method but when
I do this I get this funny error.

"An unhandled exception of type
'System.Runtime.Serialization.SerializationException' occurred in
Unknown Module.

Additional information: The type System.Web.HttpException in Assembly
System.Web, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a is not marked as serializable."

Here is a rough example of the code. There is no difference if I move
the Session=null
statement before or after the thread, or if I move the email code into
a separate class.

private void MultipleAttachEmail_Click(object sender, System.EventArgs
e)
{
ThreadStart ts = new ThreadStart(multipleMail);
Thread thread = new Thread(ts);
thread.Start();
Session["BasketSession"] = null;
Response.Redirect("DocBasketCheckout.aspx?%^="+EmailAddress.Text);
}

private void multipleMail ()
{
MailMessage msgMail = new MailMessage();
msgMail.To = EmailAddress.Text;
msgMail.From = "(e-mail address removed)";
msgMail.Subject = "Document Basket Contents";
msgMail.BodyFormat = MailFormat.Text;
msgMail.Body = " ";

// Gets document location path from Document Basket.
foreach(DataGridItem dgi in documentDataGrid.Items)
{
string dLink = baseURL + dgi.Cells[2].Text;
msgMail.Attachments.Add(new MailAttachment(Server.MapPath( dLink
)));
}
SmtpMail.SmtpServer = "127.0.0.1";
SmtpMail.Send(msgMail);
}
 
A

Alvin Bruney [MVP]

Not exactly. The error here is that this line

// Gets document location path from Document Basket.
foreach(DataGridItem dgi in documentDataGrid.Items)

is contained inside the thread code. and it operates on the datagrid. the
datagrid is owned by the main thread. A child thread cannot touch a main
thread's object in a thread safe manner. If this were a winforms
application, the fix would imply calling control.Invoke. Sadly, this is not
available in the webforms architecture. Typically, this call will work about
40 - 50 percent of the time on a NT architecture and even higher on pre NT
architectures, but it is still wrong and rightly avoided. The work around is
to not manipulate the datagrid inside the thread. A more sophisticated
technique of passing in the reference to the datagrid to the thread exists
but it isn't exactly warranted here.

--
Regards,
Alvin Bruney [ASP.NET MVP]
Got tidbits? Get it here...
http://tinyurl.com/27cok
Rick Strahl said:
Hi Matt,

This is not directly addressing your question, but I think it might very
well be related.

You'll want to be real careful with threads in this scenario. I think the
failure you're seeing might have nothing to do with clearing the session
but
something that's failing inside that thread method (for example the
DataGrid
access).

The problem here is that when you fire off a new thread and you point it
at
the method in question the class that it belongs to or some of hte
components on it might no longer be there because the page and the class
that goes with it has terminated already. One thing you can do work around
this is a use a static method.

In your scenario I would probably build the message on the ASP.Net thread
(because it relies on some data from the page) and fire only the sending
on
the new thread and stick that into a generic static method. IOW, minimize
the reliance of the thread method on anything from the main ASP.Net page
which may be terminated/ing when the thread executes.

+++ Rick ---

--

Rick Strahl
West Wind Technologies
http://www.west-wind.com/
http://www.west-wind.com/weblog/
----------------------------------
Making waves on the Web


MattB said:
Hello

I am starting a new thread in a button click event.

This thread calls an method which sends emails, I don't want the page
to wait for the emails to finish going out as it slows the user down.

I have to set a session to null in the same click method but when
I do this I get this funny error.

"An unhandled exception of type
'System.Runtime.Serialization.SerializationException' occurred in
Unknown Module.

Additional information: The type System.Web.HttpException in Assembly
System.Web, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a is not marked as serializable."

Here is a rough example of the code. There is no difference if I move
the Session=null
statement before or after the thread, or if I move the email code into
a separate class.

private void MultipleAttachEmail_Click(object sender, System.EventArgs
e)
{
ThreadStart ts = new ThreadStart(multipleMail);
Thread thread = new Thread(ts);
thread.Start();
Session["BasketSession"] = null;
Response.Redirect("DocBasketCheckout.aspx?%^="+EmailAddress.Text);
}

private void multipleMail ()
{
MailMessage msgMail = new MailMessage();
msgMail.To = EmailAddress.Text;
msgMail.From = "(e-mail address removed)";
msgMail.Subject = "Document Basket Contents";
msgMail.BodyFormat = MailFormat.Text;
msgMail.Body = " ";

// Gets document location path from Document Basket.
foreach(DataGridItem dgi in documentDataGrid.Items)
{
string dLink = baseURL + dgi.Cells[2].Text;
msgMail.Attachments.Add(new MailAttachment(Server.MapPath( dLink
)));
}
SmtpMail.SmtpServer = "127.0.0.1";
SmtpMail.Send(msgMail);
}
 
R

Rick Strahl [MVP]

Hi Alvin,

You're right of course and that's sort of what I was getting at <g>...

In this case though I'm certain that the problem is that hte page is gone by
the time the thread fires up. Matt does a Redirect() immediately following
the Thread creation which exits the page and recylces the thread. This is
likely to happen way before the new thread even starts. So if there's any
reliance on anything from the ASP page it's not likely to be there.

I've run into this in a few of my own applications and it's really a bitch
to catch if you don't know this is happening because in most cases you don't
get a failure in teh ASP.Net page because *it* completed fine. The exception
happens on another thread unknown to the ASP.Net runtime and thus you get no
messages. The thread falls down and goes away and you get an eventlog entry
for this, but otherwise nothing.

In general I would say this is a bad idea unless you always force your state
to the thread object itself and/or you call fully selfcontained static code.

Another possibly better alternative is to use Asynchronous Requests...

+++ Rick ---
 
A

Alvin Bruney [MVP]

well as rick pointed out, the other issue is that by the time the thread is
finished, the page is gone. I've seen others put a sleep in the main page or
a join to force the main thread to wait on the child thread to execute.
Infact, i've used the join which seems to work well.

Even in OP's case, the thread is still touching the response object which is
a main thread object.

--
Regards,
Alvin Bruney [ASP.NET MVP]
Got tidbits? Get it here...
http://tinyurl.com/27cok
Sharon said:
maybe this way will work:

aspx page
=========
<%@ Page Language="cs" %>
<%@ import namespace="System.Threading" %>
<%@ import namespace="test" %>

<%
string[] passArr = new string[2]{"arr val 1", "arr val 2"};

ThreadProc tp = new ThreadProc(passArr);
Thread t = new Thread(new ThreadStart(tp.ThreadProcStart));
t.Start();
%>
<html>
<head>
</head>
<body>
</body>
</html>

thread class
==========
using System.Threading;
using System.IO;

namespace test
{
public class ThreadProc
{
private string[] m_dispStrArr;

public ThreadProc(string[] inStrArr) {
m_dispStrArr = inStrArr;
}

public void ThreadProcStart() {
for (int i = 0; i < 20; i++)
{
StreamWriter fsw =
File.AppendText(System.AppDomain.CurrentDomain.BaseDirectory +
"\\log.txt");
fsw.WriteLine("m_dispStr: " + m_dispStrArr[0] + " " + m_dispStrArr[1]
+ " i: " + i);
fsw.Close();
fsw = null;

Thread.Sleep(1000);
}
}
}
}

it works but, maybe you can find a flaw?


Rick Strahl said:
Hi Alvin,

You're right of course and that's sort of what I was getting at <g>...

In this case though I'm certain that the problem is that hte page is gone by
the time the thread fires up. Matt does a Redirect() immediately
following
the Thread creation which exits the page and recylces the thread. This is
likely to happen way before the new thread even starts. So if there's any
reliance on anything from the ASP page it's not likely to be there.

I've run into this in a few of my own applications and it's really a
bitch
to catch if you don't know this is happening because in most cases you don't
get a failure in teh ASP.Net page because *it* completed fine. The exception
happens on another thread unknown to the ASP.Net runtime and thus you get no
messages. The thread falls down and goes away and you get an eventlog entry
for this, but otherwise nothing.

In general I would say this is a bad idea unless you always force your state
to the thread object itself and/or you call fully selfcontained static code.

Another possibly better alternative is to use Asynchronous Requests...

+++ Rick ---
 
S

Sharon

maybe this way will work:

aspx page
=========
<%@ Page Language="cs" %>
<%@ import namespace="System.Threading" %>
<%@ import namespace="test" %>

<%
string[] passArr = new string[2]{"arr val 1", "arr val 2"};

ThreadProc tp = new ThreadProc(passArr);
Thread t = new Thread(new ThreadStart(tp.ThreadProcStart));
t.Start();
%>
<html>
<head>
</head>
<body>
</body>
</html>

thread class
==========
using System.Threading;
using System.IO;

namespace test
{
public class ThreadProc
{
private string[] m_dispStrArr;

public ThreadProc(string[] inStrArr) {
m_dispStrArr = inStrArr;
}

public void ThreadProcStart() {
for (int i = 0; i < 20; i++)
{
StreamWriter fsw =
File.AppendText(System.AppDomain.CurrentDomain.BaseDirectory + "\\log.txt");
fsw.WriteLine("m_dispStr: " + m_dispStrArr[0] + " " + m_dispStrArr[1]
+ " i: " + i);
fsw.Close();
fsw = null;

Thread.Sleep(1000);
}
}
}
}

it works but, maybe you can find a flaw?
 
M

MattB

Thanks for all the feedback.

Decoupling - I guess thats the word for it - the datagrid from the
main thread and filling a separate string array or arraylist to pass
to a separate email class did the trick. I cannot wait in my asp.net
page codebehind to wait for a thread.join. My only remaining query is
there anything I should do in the separate email class to close the
thread, I assume garbage collection will take care of this
anyhow....Also maybe I could start the thread in the separate email
class..

here is current code sample...

private void MultipleAttachEmail_Click(object sender, System.EventArgs
e)
{
foreach(DataGridItem dgi in documentDataGrid.Items)
{
attachments.Add(Server.MapPath(baseURL + dgi.Cells[2].Text));
}
DocumentsEmail dem = new
DocumentsEmail(attachments,EmailAddress.Text);
ThreadStart ts = new ThreadStart(dem.SendMultipleAttachEmail);
thread = new System.Threading.Thread(ts);
thread.Start();
Session["BasketSession"] = null;
Response.Redirect("DocBasketCheckout.aspx?%^="+EmailAddress.Text);
}

the new DocumentsEmail class

using System;
using System.Collections;
using System.Threading;
using System.Web.Mail;
using System.Web;

namespace Org.Web.UI
{
public class DocumentsEmail
{
private ArrayList _intArrList;
private string _email;

public DocumentsEmail(ArrayList inArrList, string inEmail)
{
_intArrList = inArrList;
_email = inEmail;
}

public void SendMultipleAttachEmail()
{
MailMessage msgMail = new MailMessage();
msgMail.To = _email;
msgMail.From = "Org Website" + " <[email protected]>";
msgMail.Subject = "Your Documents";
msgMail.BodyFormat = MailFormat.Text;
msgMail.Body = " ";

// Gets document location path from Document Basket.
foreach(string dgi in _intArrList)
{
msgMail.Attachments.Add(new MailAttachment(dgi));
}

SmtpMail.SmtpServer = "192.168.0.253";
SmtpMail.Send(msgMail);
}
}

Alvin Bruney said:
well as rick pointed out, the other issue is that by the time the thread is
finished, the page is gone. I've seen others put a sleep in the main page or
a join to force the main thread to wait on the child thread to execute.
Infact, i've used the join which seems to work well.

Even in OP's case, the thread is still touching the response object which is
a main thread object.

--
Regards,
Alvin Bruney [ASP.NET MVP]
Got tidbits? Get it here...
http://tinyurl.com/27cok
Sharon said:
maybe this way will work:

aspx page
=========
<%@ Page Language="cs" %>
<%@ import namespace="System.Threading" %>
<%@ import namespace="test" %>

<%
string[] passArr = new string[2]{"arr val 1", "arr val 2"};

ThreadProc tp = new ThreadProc(passArr);
Thread t = new Thread(new ThreadStart(tp.ThreadProcStart));
t.Start();
%>
<html>
<head>
</head>
<body>
</body>
</html>

thread class
==========
using System.Threading;
using System.IO;

namespace test
{
public class ThreadProc
{
private string[] m_dispStrArr;

public ThreadProc(string[] inStrArr) {
m_dispStrArr = inStrArr;
}

public void ThreadProcStart() {
for (int i = 0; i < 20; i++)
{
StreamWriter fsw =
File.AppendText(System.AppDomain.CurrentDomain.BaseDirectory +
"\\log.txt");
fsw.WriteLine("m_dispStr: " + m_dispStrArr[0] + " " + m_dispStrArr[1]
+ " i: " + i);
fsw.Close();
fsw = null;

Thread.Sleep(1000);
}
}
}
}

it works but, maybe you can find a flaw?


Rick Strahl said:
Hi Alvin,

You're right of course and that's sort of what I was getting at <g>...

In this case though I'm certain that the problem is that hte page is gone by
the time the thread fires up. Matt does a Redirect() immediately
following
the Thread creation which exits the page and recylces the thread. This is
likely to happen way before the new thread even starts. So if there's any
reliance on anything from the ASP page it's not likely to be there.

I've run into this in a few of my own applications and it's really a
bitch
to catch if you don't know this is happening because in most cases you don't
get a failure in teh ASP.Net page because *it* completed fine. The exception
happens on another thread unknown to the ASP.Net runtime and thus you get no
messages. The thread falls down and goes away and you get an eventlog entry
for this, but otherwise nothing.

In general I would say this is a bad idea unless you always force your state
to the thread object itself and/or you call fully selfcontained static code.

Another possibly better alternative is to use Asynchronous Requests...

+++ Rick ---
 
S

Sharon

i think a separate class will work because the aspx instance
is holding reference to the array, and is passing that reference to the
class.
when the aspx is gone, the reference will no longer exist,
but the array data in the heap will still exist because the thread class is
still holding
reference to it.
when the thread class finishes execution, the array data will be collected.
true?

Alvin Bruney said:
well as rick pointed out, the other issue is that by the time the thread is
finished, the page is gone. I've seen others put a sleep in the main page or
a join to force the main thread to wait on the child thread to execute.
Infact, i've used the join which seems to work well.

Even in OP's case, the thread is still touching the response object which is
a main thread object.

--
Regards,
Alvin Bruney [ASP.NET MVP]
Got tidbits? Get it here...
http://tinyurl.com/27cok
Sharon said:
maybe this way will work:

aspx page
=========
<%@ Page Language="cs" %>
<%@ import namespace="System.Threading" %>
<%@ import namespace="test" %>

<%
string[] passArr = new string[2]{"arr val 1", "arr val 2"};

ThreadProc tp = new ThreadProc(passArr);
Thread t = new Thread(new ThreadStart(tp.ThreadProcStart));
t.Start();
%>
<html>
<head>
</head>
<body>
</body>
</html>

thread class
==========
using System.Threading;
using System.IO;

namespace test
{
public class ThreadProc
{
private string[] m_dispStrArr;

public ThreadProc(string[] inStrArr) {
m_dispStrArr = inStrArr;
}

public void ThreadProcStart() {
for (int i = 0; i < 20; i++)
{
StreamWriter fsw =
File.AppendText(System.AppDomain.CurrentDomain.BaseDirectory +
"\\log.txt");
fsw.WriteLine("m_dispStr: " + m_dispStrArr[0] + " " + m_dispStrArr[1]
+ " i: " + i);
fsw.Close();
fsw = null;

Thread.Sleep(1000);
}
}
}
}

it works but, maybe you can find a flaw?


Rick Strahl said:
Hi Alvin,

You're right of course and that's sort of what I was getting at <g>...

In this case though I'm certain that the problem is that hte page is
gone
by
the time the thread fires up. Matt does a Redirect() immediately
following
the Thread creation which exits the page and recylces the thread. This is
likely to happen way before the new thread even starts. So if there's any
reliance on anything from the ASP page it's not likely to be there.

I've run into this in a few of my own applications and it's really a
bitch
to catch if you don't know this is happening because in most cases you don't
get a failure in teh ASP.Net page because *it* completed fine. The exception
happens on another thread unknown to the ASP.Net runtime and thus you
get
no
messages. The thread falls down and goes away and you get an eventlog entry
for this, but otherwise nothing.

In general I would say this is a bad idea unless you always force your state
to the thread object itself and/or you call fully selfcontained static code.

Another possibly better alternative is to use Asynchronous Requests...

+++ Rick ---
 

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

Top